(*s: semgrep/matching/Generic_vs_generic.ml *)
(*s: pad/r2c copyright *)
(* Yoann Padioleau
 *
 * Copyright (C) 2019-2020 r2c
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation, with the
 * special exception on linking described in file license.txt.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the file
 * license.txt for more details.
 *)
(*e: pad/r2c copyright *)
open Common

(* A is the pattern, and B the concrete source code. For now
 * we both use the same module but they may differ later
 * as the expressivity of the pattern language grows.
 *
 * subtle: use 'b' to report errors, because 'a' is the sgrep pattern and it
 * has no file information usually.
 *)
module A = AST_generic
module B = AST_generic

module MV = Metavars_generic
module AST = AST_generic
module Lib = Lib_AST
module Flag = Flag_semgrep

open Matching_generic

(*****************************************************************************)
(* Prelude *)
(*****************************************************************************)
(* AST generic vs AST generic code matcher.
 *
 * This module allows to match some AST elements against other AST elements in
 * a flexible way, providing a kind of grep but at a syntactical level.
 *
 * Most of the boilerplate code was generated by
 *    $ pfff/meta/gen_code -matcher_gen_all
 * using OCaml pad-style reflection (see commons/OCaml.ml) on
 * h_program-lang/AST_generic.ml.
 *
 * See pfff/matcher/fuzzy_vs_fuzzy.ml for another approach.
 *
 * There are four main features allowing a "pattern" to match some "code":
 *  - metavariables can match anything (see metavar: tag in this file)
 *  - '...' can match any sequence (see dots: tag)
 *  - simple constructs match complex constructs having more details
 *    (e.g., the absence of attribute in a pattern will still match functions
 *     having many attributes) (see less-is-ok: tag)
 *  - the underlying AST uses some normalization (!= is transformed in !(..=))
 *    to support certain code equivalences (see equivalence: tag)
 *  - we do not care about differences in spaces/indentations/comments.
 *    we work at the AST-level.
 *
 * alternatives:
 *  - would it be simpler to work on a simpler AST, like a Term language,
 *    or even a Node/Leaf? or Ast_fuzzy? the "less-is-ok" would be
 *    difficult with that approach, because you need to know that some
 *    parts of the AST are attributes/annotations that can be skipped.
 *    In the same way, code equivalences like name resolution on the AST
 *    would be more difficult with an untyped-general tree.
 *
 *)

(*****************************************************************************)
(* Extra Helpers *)
(*****************************************************************************)

(*s: function [[Generic_vs_generic.m_string_xhp_text]] *)
(* equivalence: on different indentation
 * todo? work? was copy-pasted from XHP sgrep matcher
*)
let m_string_xhp_text sa sb =
  if  sa =$= sb || (sa =~ "^[\n ]+$" && sb =~ "^[\n ]+$")
  then return ()
  else fail ()
(*e: function [[Generic_vs_generic.m_string_xhp_text]] *)

(* ugly, see comment in Semgrep_generic.match_sts_sts *)
let env_add_matched_stmt st tin =
  let key = MV.matched_statements_special_mvar in
  match List.assoc_opt key tin with
  | None -> [tin]
  | Some (B.Ss xs) ->
      let xs' = st::xs in
      let tin = (key, B.Ss xs')::(List.remove_assoc key tin) in
      [tin]
  | Some _ -> raise Impossible

(* less: could be made more general by taking is_dots function parameter *)
let has_ellipis_and_filter_ellispis xs =
  let has_ellipsis = ref false in
  let ys = xs |> Common.exclude (function
    | A.Ellipsis _ -> has_ellipsis := true; true
    | _ -> false) in
  !has_ellipsis, ys

(*****************************************************************************)
(* Name *)
(*****************************************************************************)

(*s: function [[Generic_vs_generic.m_ident]] *)
(* coupling: modify also m_ident_and_id_info_add_in_env_Expr *)
let m_ident a b =
  match a, b with
  (*s: [[Generic_vs_generic.m_ident()]] metavariable case *)
  (* metavar: *)
  | (str, tok), b when MV.is_metavar_name str ->
      (* note that adding B.I here is sometimes not what you want.
       * this can prevent this ID to later be matched against
       * an ID used in an expression context (an Id).
       * see m_ident_and_id_info_add_in_env_Expr for more information.
       *)
      envf (str, tok) (B.I b)
  (*e: [[Generic_vs_generic.m_ident()]] metavariable case *)
  (*s: [[Generic_vs_generic.m_ident()]] regexp case *)
  (* in some languages such as Javascript certain entities like
   * fields can use strings for identifiers (e.g., {"myfield": 1}),
   * which gives the opportunity to use regexp string for fields
   * (e.g., {"=~/.*field/": $X}).
   *)
  | (stra, _), (strb, _) when Matching_generic.is_regexp_string stra ->
      let re = Matching_generic.regexp_of_regexp_string stra in
      if Str.string_match re strb 0
      then return ()
      else fail ()

  (*e: [[Generic_vs_generic.m_ident()]] regexp case *)

  (* general case *)
  | (a, b) -> (m_wrap m_string) a b
(*e: function [[Generic_vs_generic.m_ident]] *)


(*s: function [[Generic_vs_generic.m_dotted_name]] *)
let m_dotted_name a b =
  match a, b with
  (* TODO: [$X] should match any list *)
  (a, b) -> (m_list m_ident) a b
(*e: function [[Generic_vs_generic.m_dotted_name]] *)


(*s: function [[Generic_vs_generic.m_qualified_name]] *)
let m_qualified_name a b =
  match a, b with
  (a, b) -> m_dotted_name a b
(*e: function [[Generic_vs_generic.m_qualified_name]] *)

(*s: function [[Generic_vs_generic.m_module_name_prefix]] *)
(* less-is-ok: prefix matching is supported for imports, eg.:
 *  pattern: import foo.x should match: from foo.x.z.y
 *)
let m_module_name_prefix a b =
  match a, b with
  (* metavariable case *)
  | A.FileName((a_str, _) as a1), B.FileName(b1) when MV.is_metavar_name a_str ->
    (* Bind as a literal string expression so that pretty-printing works.
     * This also means that this metavar can match both literal strings and filenames
     * with the same string content. *)
    envf a1 (B.E (B.L (B.String b1)))
  | A.FileName(a1), B.FileName(b1) ->
    (* TODO figure out what prefix support means here *)
    (m_wrap m_string_prefix) a1 b1
  | A.DottedName(a1), B.DottedName(b1) ->
    m_list_prefix m_ident a1 b1
  | A.FileName _, _
  | A.DottedName _, _
   -> fail ()
(*e: function [[Generic_vs_generic.m_module_name_prefix]] *)

(*s: function [[Generic_vs_generic.m_module_name]] *)
let m_module_name a b =
  match a, b with
  | A.FileName(a1), B.FileName(b1) ->
    (m_wrap m_string) a1 b1
  | A.DottedName(a1), B.DottedName(b1) ->
    m_dotted_name a1 b1
  | A.FileName _, _
  | A.DottedName _, _
   -> fail ()
(*e: function [[Generic_vs_generic.m_module_name]] *)

(*s: function [[Generic_vs_generic.m_sid]] *)
let m_sid a b =
  if a =|= b then return () else fail ()
(*e: function [[Generic_vs_generic.m_sid]] *)

(*s: function [[Generic_vs_generic.m_resolved_name_kind]] *)
let m_resolved_name_kind a b =
  match a, b with
  | A.Local, B.Local ->
      return ()
  | A.EnclosedVar, B.EnclosedVar ->
      return ()
  | A.Param, B.Param ->
      return ()
  | A.Global, B.Global ->
      return ()
  | A.ImportedEntity(a1), B.ImportedEntity(b1) ->
    m_qualified_name a1 b1
  | A.ImportedModule(a1), B.ImportedModule(b1) ->
    m_module_name a1 b1
  | A.Macro, B.Macro ->
    return ()
  | A.EnumConstant, B.EnumConstant ->
    return ()
  | A.TypeName, B.TypeName ->
    return ()

  | A.Local , _
  | A.Param , _
  | A.Global, _
  | A.EnclosedVar , _
  | A.Macro, _
  | A.EnumConstant, _
  | A.TypeName, _
  | A.ImportedEntity _, _
  | A.ImportedModule _, _
   -> fail ()
(*e: function [[Generic_vs_generic.m_resolved_name_kind]] *)

(*s: function [[Generic_vs_generic._m_resolved_name]] *)
let _m_resolved_name (a1, a2) (b1, b2) =
  m_resolved_name_kind a1 b1 >>= (fun () ->
  m_sid a2 b2 )
(*e: function [[Generic_vs_generic._m_resolved_name]] *)


(* start of recursive need *)
(*s: function [[Generic_vs_generic.m_name]] *)
let rec m_name a b =
  match a,b with
  | (a1, a2), (b1, b2) ->
    m_ident a1 b1 >>= (fun () ->
    m_name_info a2 b2
    )
(*e: function [[Generic_vs_generic.m_name]] *)

(*s: function [[Generic_vs_generic.m_ident_and_id_info_add_in_env_Expr]] *)
and m_ident_and_id_info_add_in_env_Expr (a1, a2) (b1, b2) =
  (* metavar: *)
  match a1, b1 with
  | (str, tok), b when MV.is_metavar_name str ->
      m_id_info a2 b2 >>= (fun () ->
        envf (str, tok) (B.E (B.Id (b, b2))) (* B.E here, not B.I *)
     )
  (* same code than for m_ident *)
  (*s: [[Generic_vs_generic.m_ident()]] regexp case *)
  (* in some languages such as Javascript certain entities like
   * fields can use strings for identifiers (e.g., {"myfield": 1}),
   * which gives the opportunity to use regexp string for fields
   * (e.g., {"=~/.*field/": $X}).
   *)
  | (stra, _), (strb, _) when Matching_generic.is_regexp_string stra ->
      let re = Matching_generic.regexp_of_regexp_string stra in
      if Str.string_match re strb 0
      then return ()
      else fail ()

  (*e: [[Generic_vs_generic.m_ident()]] regexp case *)
  (* general case *)
  | (a, b) -> (m_wrap m_string) a b
(*e: function [[Generic_vs_generic.m_ident_and_id_info_add_in_env_Expr]] *)

and m_ident_and_empty_id_info_add_in_env_Expr a1 b1 =
  let empty = AST.empty_id_info () in
  m_ident_and_id_info_add_in_env_Expr (a1, empty) (b1, empty)

(*s: function [[Generic_vs_generic.m_id_info]] *)
and m_id_info a b =
  match a, b with
  { A. id_resolved = _a1; id_type = _a2; id_const_literal = _a3 },
  { B. id_resolved = _b1; id_type = _b2; id_const_literal = _b3 }
   ->
     (* old: (m_ref m_resolved_name) a3 b3  >>= (fun () ->
      * but doing import flask in a source file means every reference
      * to flask.xxx will be tagged with a ImportedEntity, but
      * semgrep pattern will use flask.xxx directly, without the preceding
      * import, without this tag, which would prevent
      * matching. We need to correctly resolve names and always compare with
      * the resolved_name instead of the name used in the code
      * (which can be an alias)
      *
      * Note that is is independent of the check done in equal_ast to check
      * that two $X refers to the same code. In that case we are using
      * the id_resolved tag and sid.
      *)
     (* old: (m_ref (m_option m_type_)) a2 b2
      * the same is true for types! Now we sometimes propagate type annotations
      * in Naming_AST.ml, but we do that in the source file, not the pattern,
      * which would prevent a match.
      * More generally, id_info is something populated and used on the
      * generic AST of the source, not on the pattern, hence we should
      * not use it as a condition for matching here. Instead use
      * the information in the caller.
      *)
      return ()
(*e: function [[Generic_vs_generic.m_id_info]] *)

(*****************************************************************************)
(* Expression *)
(*****************************************************************************)

(*s: function [[Generic_vs_generic.make_dotted]] *)
and make_dotted xs =
  match xs with
  | [] -> raise Impossible
  | x::xs ->
    let base = B.Id (x, B.empty_id_info()) in
    List.fold_left (fun acc e ->
      let tok = Parse_info.fake_info "." in
      B.DotAccess (acc, tok, B.FId e)) base xs
(*e: function [[Generic_vs_generic.make_dotted]] *)

(* possibly go deeper when someone wants that a pattern like
 *   'bar();'
 * match also an expression statement like
 *   'x = bar();'.
 *
 * This is very hacky.
 *
 * alternatives:
 *  - force the user to use 'if(... <expr> ...)' (isaac, jmelton)
 *  - do as in coccinelle and use 'if(<... <expr> ...>)'
 *  - CURRENT: impicitely go deep without requiring an extra syntax
 *
 * todo? we could restrict ourselves to only a few forms? see SubAST_generic.ml
 *   - x = <expr>,
 *   - <call>(<exprs).
 *)
(* experimental! *)
(*s: function [[Generic_vs_generic.m_expr_deep]] *)
and m_expr_deep a b =
  if not !Flag.go_deeper_expr
  then m_expr a b
  else
    m_expr a b >!> (fun () ->
      let subs = SubAST_generic.subexprs_of_expr b in
      (* less: could use a fold *)
      let rec aux xs =
        match xs with
        | [] -> fail ()
        | x::xs ->
           m_expr_deep a x >||> aux xs
      in
      aux subs
    )
(*e: function [[Generic_vs_generic.m_expr_deep]] *)


(* coupling: if you add special sgrep hooks here, you should probably
 * also add them in m_pattern
 *)
(*s: function [[Generic_vs_generic.m_expr]] *)
and m_expr a b =
  match a, b with
  (* the order of the matches matters! take care! *)
  (*s: [[Generic_vs_generic.m_expr()]] disjunction case *)
  (* equivalence: user-defined equivalence! *)
  | A.DisjExpr (a1, a2), b ->
      m_expr a1 b >||> m_expr a2 b
  (*e: [[Generic_vs_generic.m_expr()]] disjunction case *)
  (*s: [[Generic_vs_generic.m_expr()]] resolving alias case *)
  (* equivalence: name resolving! *)
  | a,   B.Id (_, { B.id_resolved =
      {contents = Some ( ( B.ImportedEntity dotted
                         | B.ImportedModule (B.DottedName dotted)
                         ), _sid)}; _}) ->
    m_expr a (make_dotted dotted)
  (* Put this before the next case to prevent overly eager dealiasing *)
  | A.IdQualified(a1, a2), B.IdQualified(b1, b2) ->
    m_name a1 b1 >>= (fun () ->
    m_id_info a2 b2
    )
  (* Matches pattern
   *   a.b.C.x
   * to code
   *   import a.b.C
   *   C.x
   *)
  | A.IdQualified ((alabel, { A.name_qualifier = Some(A.QDots names); _ }), _id_info), b ->
    let full = names @ [alabel] in
    m_expr (make_dotted full) b
  (*e: [[Generic_vs_generic.m_expr()]] resolving alias case *)
  (*s: [[Generic_vs_generic.m_expr()]] metavariable case *)
  (*s: [[Generic_vs_generic.m_expr()]] forbidden metavariable case *)
  (* $X should not match an IdSpecial otherwise $X(...) could match
   * a+b because this is transformed in a Call(IdSpecial Plus, ...)
   *)
  | A.Id ((str,_tok), _id_info), B.IdSpecial _
      when MV.is_metavar_name str ->
      fail ()
  (*e: [[Generic_vs_generic.m_expr()]] forbidden metavariable case *)
  (* metavar: *)
  | A.Id ((str,tok), _id_info), e2
     when MV.is_metavar_name str ->
      envf (str, tok) (B.E (e2))
  (*e: [[Generic_vs_generic.m_expr()]] metavariable case *)
  (*s: [[Generic_vs_generic.m_expr()]] typed metavariable case *)
  (* metavar: typed! *)
  | A.TypedMetavar ((str, tok), _, t), e2
      when MV.is_metavar_name str ->
           m_compatible_type (str, tok) t e2
  (*e: [[Generic_vs_generic.m_expr()]] typed metavariable case *)
  (*s: [[Generic_vs_generic.m_expr()]] ellipsis cases *)
  (* dots: should be patterned-match before in arguments, or statements,
   * but this is useful for keyword parameters, as in f(..., foo=..., ...)
   *)
  | A.Ellipsis(_a1), _ ->
    return ()
  (*x: [[Generic_vs_generic.m_expr()]] ellipsis cases *)
  | A.DeepEllipsis (_, a1, _), a2 ->
      m_expr_deep a1 a2
  (*e: [[Generic_vs_generic.m_expr()]] ellipsis cases *)

  (* must be before constant propagation case below *)
  | A.L(a1), B.L(b1) ->
    m_literal a1 b1

  (*s: [[Generic_vs_generic.m_expr()]] propagated constant case *)
  (* equivalence: constant propagation and evaluation!
   * TODO: too late, must do that before 'metavar:' so that
   * const a = "foo"; ... a == "foo" would be catched by $X == $X.
   *)
  | A.L(a1), b1 ->
    (match Normalize_generic.constant_propagation_and_evaluate_literal b1 with
    | Some b1 ->
      m_literal a1 b1
    | None -> fail ()
    )
  (*e: [[Generic_vs_generic.m_expr()]] propagated constant case *)

  (*s: [[Generic_vs_generic.m_expr()]] sequencable container cases *)
  | A.Container(A.Array, a2), B.Container(B.Array, b2) ->
    (m_bracket (m_container_ordered_elements)) a2 b2
  | A.Container(A.List, a2), B.Container(B.List, b2) ->
    (m_bracket (m_container_ordered_elements)) a2 b2
  | A.Tuple(a1), B.Tuple(b1) ->
    m_container_ordered_elements a1 b1
  (*e: [[Generic_vs_generic.m_expr()]] sequencable container cases *)
  | A.Container((A.Set | A.Dict) as a1, (_, a2, _)),
    B.Container((B.Set | B.Dict) as b1, (_, b2, _)) ->
    m_container_set_or_dict_unordered_elements (a1, a2) (b1, b2)

  (*s: [[Generic_vs_generic.m_expr()]] interpolated strings case *)
  (* dots '...' for string literal:
   * Interpolated strings are transformed into Call(Special(Concat, ...),
   * hence we want Python patterns like f"...{$X}...", which are expanded to
   * Call(Special(Concat, [L"..."; Id "$X"; L"..."])) to
   * match concrete code like f"foo{a}" such that "..." is seemingly
   * matching 0 or more literal expressions.
   * bugfix: note that we want to do that only when inside
   * Call(Special(Concat(...))), hence the special m_arguments_concat below,
   * otherwise regular call patterns like foo("...") would match code like
   * foo().
   *)
  | A.Call(A.IdSpecial(A.ConcatString akind, _a1), a2),
    B.Call(B.IdSpecial(B.ConcatString bkind, _b1), b2) ->
    m_concat_string_kind akind bkind >>= (fun () ->
    m_bracket m_arguments_concat a2 b2
    )
  (*e: [[Generic_vs_generic.m_expr()]] interpolated strings case *)
  (* The pattern '$X = 1 + 2 + ...' is parsed as '$X = (1 + 2) + ...', but
   * if the concrete code is 'foo = 1 + 2 + 3 + 4', this will naively not
   * match because (1+2)+... will be matched against ((1+2)+3)+4 and fails.
   * The ellipsis operator with binary operators should be more flexible
   * and allows any number of additional arguments, which when translated
   * in Call() means we need to go deeper.
   *)

  | A.Call(A.IdSpecial(A.Op aop, _toka),
      (_, [A.Arg a1;A.Arg(A.Ellipsis _tdots)], _)),
    B.Call(B.IdSpecial(B.Op bop, _tokb),
      (_, [B.Arg b1; B.Arg _b2], _)) ->
     m_arithmetic_operator aop bop >>= (fun () ->
       m_expr a1 b1 >!> (fun () ->
         (* try again deeper on b1 *)
         m_expr a b1
     ))

  (* boilerplate *)
  | A.Id(a1, a2), B.Id(b1, b2) ->
    m_ident a1 b1 >>= (fun () ->
    m_id_info a2 b2 )

  | A.Call(a1, a2), B.Call(b1, b2) ->
    m_expr a1 b1 >>= (fun () ->
    m_arguments a2 b2
    )

  | A.Assign(a1, at, a2), B.Assign(b1, bt, b2) ->
    m_expr a1 b1 >>= (fun () ->
    m_tok at bt >>= (fun () ->
    m_expr a2 b2
    ))

  | A.DotAccess(a1, at, a2), B.DotAccess(b1, bt, b2) ->
    m_expr a1 b1 >>= (fun () ->
    m_tok at bt >>= (fun () ->
    m_field_ident a2 b2
    ))

  | A.ArrayAccess(a1, a2), B.ArrayAccess(b1, b2) ->
    m_expr a1 b1 >>= (fun () ->
    m_expr a2 b2
    )

  (*s: [[Generic_vs_generic.m_expr()]] boilerplate cases *)
    | A.Record(a1), B.Record(b1) ->
      (m_bracket (m_fields)) a1 b1
    | A.Constructor(a1, a2), B.Constructor(b1, b2) ->
      m_name a1 b1 >>= (fun () ->
      (m_list m_expr) a2 b2
      )
    | A.Lambda(a1), B.Lambda(b1) ->
      m_function_definition a1 b1 >>= (fun () ->
      return ())
    | A.AnonClass(a1), B.AnonClass(b1) ->
      m_class_definition a1 b1
    | A.IdSpecial(a1), B.IdSpecial(b1) ->
      m_wrap m_special a1 b1

    | A.AssignOp(a1, a2, a3), B.AssignOp(b1, b2, b3) ->
      m_expr a1 b1 >>= (fun () ->
      m_wrap m_arithmetic_operator a2 b2 >>= (fun () ->
      m_expr a3 b3
      ))

    | A.Xml(a1), B.Xml(b1) ->
      m_xml a1 b1
    | A.LetPattern(a1, a2), B.LetPattern(b1, b2) ->
      m_pattern a1 b1 >>= (fun () ->
      m_expr a2 b2
      )

    | A.SliceAccess(a1, a2, a3, a4), B.SliceAccess(b1, b2, b3, b4) ->
      m_expr a1 b1 >>= (fun () ->
      m_option m_expr a2 b2 >>= (fun () ->
      m_option m_expr a3 b3 >>= (fun () ->
      m_option m_expr a4 b4
      )))
    | A.Conditional(a1, a2, a3), B.Conditional(b1, b2, b3) ->
      m_expr a1 b1 >>= (fun () ->
      m_expr a2 b2 >>= (fun () ->
      m_expr a3 b3
      ))
    | A.MatchPattern(a1, a2), B.MatchPattern(b1, b2) ->
      m_expr a1 b1 >>= (fun () ->
      (m_list m_action) a2 b2
      )
    | A.Yield(a0, a1, a2), B.Yield(b0, b1, b2) ->
      m_tok a0 b0 >>= (fun () ->
      m_option m_expr a1 b1 >>= (fun () ->
      m_bool a2 b2
      ))
    | A.Await(a0, a1), B.Await(b0, b1) ->
      m_tok a0 b0 >>= (fun () ->
      m_expr a1 b1
      )
    | A.Cast(a1, a2), B.Cast(b1, b2) ->
      m_type_ a1 b1 >>= (fun () ->
      m_expr a2 b2
      )
    | A.Seq(a1), B.Seq(b1) ->
      (m_list m_expr) a1 b1
    | A.Ref(a0, a1), B.Ref(b0, b1) ->
      m_tok a0 b0 >>= (fun () ->
      m_expr a1 b1
      )
    | A.DeRef(a0, a1), B.DeRef(b0, b1) ->
      m_tok a0 b0 >>= (fun () ->
      m_expr a1 b1
      )


    | A.OtherExpr(a1, a2), B.OtherExpr(b1, b2) ->
      m_other_expr_operator a1 b1 >>= (fun () ->
      (m_list m_any) a2 b2
       )
    | A.Container _, _  | A.Tuple _, _  | A.Record _, _
    | A.Constructor _, _  | A.Lambda _, _  | A.AnonClass _, _
    | A.Id _, _  | A.IdQualified _, _ | A.IdSpecial _, _
    | A.Call _, _  | A.Xml _, _
    | A.Assign _, _  | A.AssignOp _, _  | A.LetPattern _, _  | A.DotAccess _, _
    | A.ArrayAccess _, _  | A.Conditional _, _  | A.MatchPattern _, _
    | A.Yield _, _  | A.Await _, _  | A.Cast _, _  | A.Seq _, _  | A.Ref _, _
    | A.DeRef _, _  | A.OtherExpr _, _
    | A.SliceAccess _, _
    | A.TypedMetavar _, _
     -> fail ()
  (*e: [[Generic_vs_generic.m_expr()]] boilerplate cases *)
(*e: function [[Generic_vs_generic.m_expr]] *)

(*s: function [[Generic_vs_generic.m_field_ident]] *)
and m_field_ident a b =
  match a, b with
  (* boilerplate *)
  | A.FId a, B.FId b ->
      m_ident a b
  (*s: [[Generic_vs_generic.m_field_ident()]] boilerplate cases *)
  | A.FName a, B.FName b ->
      m_name a b
  | A.FDynamic a, B.FDynamic b ->
      m_expr a b
  | A.FId _, _
  | A.FName _, _
  | A.FDynamic _, _
    -> fail ()
  (*e: [[Generic_vs_generic.m_field_ident()]] boilerplate cases *)
(*e: function [[Generic_vs_generic.m_field_ident]] *)

(*s: function [[Generic_vs_generic.m_label_ident]] *)
and m_label_ident a b =
  match a, b with
  | A.LNone, B.LNone -> return ()
  | A.LId a, B.LId b ->
      m_label a b
  | A.LInt a, B.LInt b ->
      m_wrap m_int a b
  | A.LDynamic a, B.LDynamic b ->
      m_expr a b
  | A.LNone, _
  | A.LId _, _
  | A.LInt _, _
  | A.LDynamic _, _
    -> fail ()
(*e: function [[Generic_vs_generic.m_label_ident]] *)

(*s: function [[Generic_vs_generic.m_literal]] *)
and m_literal a b =
  match a, b with
  (*s: [[Generic_vs_generic.m_literal()]] ellipsis case *)
  (* dots: '...' on string *)
  | A.String("...", a), B.String(_s, b) ->
      m_info a b
  (*e: [[Generic_vs_generic.m_literal()]] ellipsis case *)
  (*s: [[Generic_vs_generic.m_literal()]] regexp case *)
  (* regexp matching *)
  | A.String(name, info_name), B.String(sb, info_sb)
      when Matching_generic.is_regexp_string name ->
      let re = Matching_generic.regexp_of_regexp_string name in
      if Str.string_match re sb 0
      then
        m_info info_name info_sb
      else fail ()
  (*e: [[Generic_vs_generic.m_literal()]] regexp case *)

  (* boilerplate *)
  | A.String(a1), B.String(b1) ->
    (m_wrap m_string) a1 b1
  | A.Unit(a1), B.Unit(b1) ->
    m_tok a1 b1
  | A.Bool(a1), B.Bool(b1) ->
    (m_wrap m_bool) a1 b1
  | A.Int(a1), B.Int(b1) ->
    (m_wrap m_string) a1 b1
  | A.Float(a1), B.Float(b1) ->
    (m_wrap m_string) a1 b1
  | A.Imag(a1), B.Imag(b1) ->
    (m_wrap m_string) a1 b1
  | A.Ratio(a1), B.Ratio(b1) ->
    (m_wrap m_string) a1 b1
  | A.Atom(a1), B.Atom(b1) ->
    (m_wrap m_string) a1 b1
  | A.Char(a1), B.Char(b1) ->
    (m_wrap m_string) a1 b1
  | A.Regexp(("/.../", a)), B.Regexp((_s, b)) ->
    m_info a b
  | A.Regexp(a1), B.Regexp(b1) ->
    (m_wrap m_string) a1 b1
  | A.Null(a1), B.Null(b1) ->
    m_tok a1 b1
  | A.Undefined(a1), B.Undefined(b1) ->
    m_tok a1 b1

  | A.Unit _, _  | A.Bool _, _  | A.Int _, _  | A.Float _, _  | A.Char _, _
  | A.String _, _  | A.Regexp _, _  | A.Null _, _  | A.Undefined _, _
  | A.Imag _, _ | A.Ratio _, _ | A.Atom _, _
   -> fail ()
(*e: function [[Generic_vs_generic.m_literal]] *)

(*s: function [[Generic_vs_generic.m_action]] *)
and m_action a b =
  match a, b with
  | (a1, a2), (b1, b2) ->
    m_pattern a1 b1 >>= (fun () ->
    m_expr a2 b2
    )
(*e: function [[Generic_vs_generic.m_action]] *)

(*s: function [[Generic_vs_generic.m_arithmetic_operator]] *)
and m_arithmetic_operator a b =
  match a, b with
  | _ when a =*= b -> return ()
  | _ -> fail ()
(*e: function [[Generic_vs_generic.m_arithmetic_operator]] *)

(*s: function [[Generic_vs_generic.m_special]] *)
and m_special a b =
  match a, b with
  | A.This, B.This ->
    return ()
  | A.Super, B.Super ->
    return ()
  | A.Self, B.Self ->
    return ()
  | A.Parent, B.Parent ->
    return ()
  | A.Eval, B.Eval ->
    return ()
  | A.Typeof, B.Typeof ->
    return ()
  | A.Instanceof, B.Instanceof ->
    return ()
  | A.Sizeof, B.Sizeof ->
    return ()
  | A.New, B.New ->
    return ()
  | A.Defined, B.Defined ->
    return ()
  | A.ConcatString a, B.ConcatString b ->
    m_concat_string_kind a b
  | A.Spread, B.Spread ->
    return ()
  | A.HashSplat, B.HashSplat ->
    return ()
  | A.Op(a1), B.Op(b1) ->
    m_arithmetic_operator a1 b1
  | A.EncodedString(a1), B.EncodedString(b1) ->
    m_wrap m_string a1 b1
  | A.IncrDecr(a1, a2), B.IncrDecr(b1, b2) ->
    m_eq a1 b1 >>= (fun () ->
    m_eq a2 b2
    )
  | A.This, _  | A.Super, _  | A.Self, _  | A.Parent, _  | A.Eval, _
  | A.Typeof, _  | A.Instanceof, _  | A.Sizeof, _  | A.New, _
  | A.ConcatString _, _  | A.Spread, _  | A.Op _, _  | A.IncrDecr _, _
  | A.EncodedString _, _ | A.HashSplat, _ | A.Defined, _
   -> fail ()
(*e: function [[Generic_vs_generic.m_special]] *)

(* fstring pattern should match only fstring *)
and m_concat_string_kind a b =
  match a, b with
  | A.FString, B.FString -> return ()
  | A.FString, _ -> fail ()
  (* less-is-more: *)
  | _ -> return ()

(*s: function [[Generic_vs_generic.m_name_info]] *)
and m_name_info a b =
  match a, b with
  { A. name_qualifier = a1; name_typeargs = a2; },
  { B. name_qualifier = b1; name_typeargs = b2; }
   ->
    (m_option m_qualifier) a1 b1 >>= (fun () ->
    (m_option m_type_arguments) a2 b2
    )
(*e: function [[Generic_vs_generic.m_name_info]] *)

and m_qualifier a b =
  match a, b with
  | A.QDots a, B.QDots b -> m_dotted_name a b
  | A.QTop a, B.QTop b -> m_tok a b
  | A.QExpr (a1, a2), B.QExpr (b1, b2) ->
      m_expr a1 b1 >>= (fun () ->
      m_tok a2 b2
      )
  | A.QDots _, _ | A.QTop _, _ | A.QExpr _, _ -> fail ()


and m_container_set_or_dict_unordered_elements (a1, a2) (b1, b2) =
  match ((a1, a2), (b1, b2)) with
  (* those rules should be applied only for python? *)
  | ((A.Dict | A.Set), []),             ((A.Dict | A.Set), []) -> return ()
  | ((A.Dict | A.Set), [A.Ellipsis _]), ((A.Dict | A.Set), _) -> return ()
  | (A.Set, a2), (B.Set, b2) ->
        let has_ellipsis, a2 = has_ellipis_and_filter_ellispis a2 in
        m_list_in_any_order ~less_is_ok:has_ellipsis m_expr a2 b2
  | (A.Dict, a2), (B.Dict, b2) ->
        let has_ellipsis, a2 = has_ellipis_and_filter_ellispis a2 in
        m_list_in_any_order ~less_is_ok:has_ellipsis m_expr a2 b2
  | _, _ ->
    (* less: could return fail () *)
    m_container_operator a1 b1 >>= (fun () ->
    m_list m_expr a2 b2
    )

(*s: function [[Generic_vs_generic.m_container_operator]] *)
(* coupling: if you add a constructor in AST_generic.container,
 * you probrably need to update m_container_set_or_dict_unordered_elements
 * and m_expr to handle this new kind of container.
 *)
and m_container_operator a b =
  match a, b with
  (* boilerplate *)
  | A.Array, B.Array ->
    return ()
  | A.List, B.List ->
    return ()
  | A.Set, B.Set ->
    return ()
  | A.Dict, B.Dict ->
    return ()
  | A.Array, _  | A.List, _  | A.Set, _  | A.Dict, _
   -> fail ()
(*e: function [[Generic_vs_generic.m_container_operator]] *)

(*s: function [[Generic_vs_generic.m_container_ordered_elements]] *)
and m_container_ordered_elements a b =
  m_list_with_dots m_expr
    (function A.Ellipsis _ -> true | _ -> false)
  false (* empty list can not match non-empty list *)
  a b
(*e: function [[Generic_vs_generic.m_container_ordered_elements]] *)

(*s: function [[Generic_vs_generic.m_other_expr_operator]] *)
and m_other_expr_operator = m_other_xxx
(*e: function [[Generic_vs_generic.m_other_expr_operator]] *)

(*---------------------------------------------------------------------------*)
(* XML *)
(*---------------------------------------------------------------------------*)

(*s: function [[Generic_vs_generic.m_xml]] *)
and m_xml a b =
  match a, b with
  | { A.xml_tag = a1; xml_attrs = a2; xml_body = a3 },
    { B.xml_tag = b1; xml_attrs = b2; xml_body = b3 } ->
    m_ident a1 b1 >>= (fun () ->
    m_attrs a2 b2 >>= (fun () ->
    m_bodies a3 b3
    ))
(*e: function [[Generic_vs_generic.m_xml]] *)

(*s: function [[Generic_vs_generic.m_attrs]] *)
and m_attrs a b =
  m_list__m_xml_attr a b
(*e: function [[Generic_vs_generic.m_attrs]] *)

(*s: function [[Generic_vs_generic.m_bodies]] *)
and m_bodies a b =
  m_list__m_body a b
(*e: function [[Generic_vs_generic.m_bodies]] *)

(* less: use m_list_in_any_order? move split_when in it? *)
(*s: function [[Generic_vs_generic.m_list__m_xml_attr]] *)
and m_list__m_xml_attr
 (xsa: A.xml_attribute list) (xsb: A.xml_attribute list) =
  match xsa, xsb with
  | [], [] ->
      return ()

  (*s: [[Generic_vs_generic.m_list__m_xml_attr]] empty list vs list case *)
  (* less-is-ok: *)
  | [], _::_ ->
      return ()
  (*e: [[Generic_vs_generic.m_list__m_xml_attr]] empty list vs list case *)
  (* less: allow '...'? *)

  | (((s1, _), _) as a)::xsa, xsb ->
     (*s: [[Generic_vs_generic.m_list__m_xml_attr]] if metavar attribute *)
     if MV.is_metavar_name s1
     then
        let candidates = all_elem_and_rest_of_list xsb in
        (* less: could use a fold *)
        let rec aux xs =
          match xs with
          | [] -> fail ()
          | (b, xsb)::xs ->
              (m_xml_attr a b >>= (fun () -> m_list__m_xml_attr xsa xsb))
              >||> aux xs
        in
        aux candidates
     (*e: [[Generic_vs_generic.m_list__m_xml_attr]] if metavar attribute *)
     else
      (try
        let (before, there, after) = xsb |> Common2.split_when (function
            | ((s2, _), _) when s2 = s1 -> true
            | _ -> false
        ) in
        (match there with
        | b ->
           m_xml_attr a b >>= (fun () ->
           m_list__m_xml_attr xsa (before @ after)
           )
        (* | _ -> raise Impossible *)
        )
      with Not_found -> fail ()
      )
(* type matching *)

and m_compatible_type typed_mvar t e =
  match t, e with
  (* for python literal checking *)
  | A.OtherType (A.OT_Expr, [A.E (A.Id (("int", _tok), _idinfo))]),
    B.L (B.Int _) -> envf typed_mvar (B.E e)
  | A.OtherType (A.OT_Expr, [A.E (A.Id (("float", _tok), _idinfo))]),
    B.L (B.Float _) -> envf typed_mvar (B.E e)
  | A.OtherType (A.OT_Expr, [A.E (A.Id (("str", _tok), _idinfo))]),
    B.L (B.String _) -> envf typed_mvar (B.E e)
  (* for java literals *)
  | A.TyBuiltin (("int", _)),  B.L (B.Int _) -> envf typed_mvar (B.E e)
  | A.TyBuiltin (("float", _)),  B.L (B.Float _) -> envf typed_mvar (B.E e)
  | A.TyName (("String", _), _), B.L (B.String _) -> envf typed_mvar (B.E e)
  (* for go literals *)
  | A.TyName (("int", _), _), B.L (B.Int _) -> envf typed_mvar (B.E e)
  | A.TyName (("float", _), _), B.L (B.Float _) -> envf typed_mvar (B.E e)
  | A.TyName (("str", _), _), B.L (B.String _) -> envf typed_mvar (B.E e)
  (* for matching ids *)
  | t1, B.Id (_, {B.id_type; _}) ->
      (match !id_type with Some (t2) -> m_type_ t1 t2
                         | _ -> fail ())

  | _ -> fail ()

  (* the general case *)
(*
  | xa::aas, xb::bbs ->
      m_xml_attr xa xb >>= (fun () ->
      m_list__m_xml_attr aas bbs
      )
  | _::_, _ ->
      fail ()
*)
(*e: function [[Generic_vs_generic.m_list__m_xml_attr]] *)

(*s: function [[Generic_vs_generic.m_list__m_body]] *)
and m_list__m_body a b =
  match a with
  (* less-is-ok: it's ok to have an empty body in the pattern *)
  | [] -> return ()

  | _ -> m_list m_body a b
(*e: function [[Generic_vs_generic.m_list__m_body]] *)

(*s: function [[Generic_vs_generic.m_xml_attr]] *)
and m_xml_attr a b =
  match a, b with
  | (a1, a2), (b1, b2) ->
    m_ident a1 b1 >>= (fun () ->
    m_xml_attr_value a2 b2
    )
(*e: function [[Generic_vs_generic.m_xml_attr]] *)

(*s: function [[Generic_vs_generic.m_xml_attr_value]] *)
and m_xml_attr_value a b =
  (* less: deep? *)
  m_expr a b
(*e: function [[Generic_vs_generic.m_xml_attr_value]] *)

(*s: function [[Generic_vs_generic.m_body]] *)
and m_body a b =
  match a, b with
  | A.XmlText a1, B.XmlText b1 ->
      m_wrap m_string_xhp_text a1 b1
  (* boilerplate *)
  (*s: [[Generic_vs_generic.m_body]] boilerplate cases *)
    | A.XmlExpr a1, B.XmlExpr b1 ->
        m_expr a1 b1
    | A.XmlXml a1, B.XmlXml b1 ->
        m_xml a1 b1
    | A.XmlText _, _
    | A.XmlExpr _, _
    | A.XmlXml _, _
      -> fail ()
  (*e: [[Generic_vs_generic.m_body]] boilerplate cases *)
(*e: function [[Generic_vs_generic.m_body]] *)

(*---------------------------------------------------------------------------*)
(* Arguments list iso *)
(*---------------------------------------------------------------------------*)

(*s: function [[Generic_vs_generic.m_arguments]] *)
and m_arguments a b =
  match a, b with
  (a, b) -> m_bracket (m_list__m_argument) a b
(*e: function [[Generic_vs_generic.m_arguments]] *)

(* less: factorize in m_list_and_dots? but also has unordered for kwd args *)
(*s: function [[Generic_vs_generic.m_list__m_argument]] *)
and m_list__m_argument (xsa: A.argument list) (xsb: A.argument list) =
  match xsa, xsb with
  | [], [] ->
      return ()
  (*s: [[Generic_vs_generic.m_list__m_argument()]] ellipsis cases *)
  (* dots: ..., can also match no argument *)
  | [A.Arg (A.Ellipsis _i)], [] ->
      return ()

  | A.Arg (A.Ellipsis i)::xsa, xb::xsb ->
      (* can match nothing *)
      (m_list__m_argument xsa (xb::xsb)) >||>
      (* can match more *)
      (m_list__m_argument ((A.Arg (A.Ellipsis i))::xsa) xsb)
  (*e: [[Generic_vs_generic.m_list__m_argument()]] ellipsis cases *)
  (*s: [[Generic_vs_generic.m_list__m_argument()]] [[ArgKwd]] pattern case *)
  (* unordered kwd argument matching *)
  | (A.ArgKwd ((s, _tok) as ida, ea) as a)::xsa, xsb ->
     (*s: [[Generic_vs_generic.m_list__m_argument()]] if metavar keyword argument *)
     if MV.is_metavar_name s
     then
        let candidates = all_elem_and_rest_of_list xsb in
        (* less: could use a fold *)
        let rec aux xs =
          match xs with
          | [] -> fail ()
          | (b, xsb)::xs ->
              (m_argument a b >>= (fun () -> m_list__m_argument xsa xsb))
              >||> aux xs
        in
        aux candidates
     (*e: [[Generic_vs_generic.m_list__m_argument()]] if metavar keyword argument *)
     else
      (try
        let (before, there, after) = xsb |> Common2.split_when (function
            | A.ArgKwd ((s2,_), _) when s =$= s2 -> true
            | _ -> false) in
        (match there with
        | A.ArgKwd (idb, eb) ->
           m_ident ida idb >>= (fun () ->
           m_expr ea eb >>= (fun () ->
           m_list__m_argument xsa (before @ after)
           ))
        | _ -> raise Impossible
        )
      with Not_found -> fail ()
      )
  (*e: [[Generic_vs_generic.m_list__m_argument()]] [[ArgKwd]] pattern case *)
  (* the general case *)
  | xa::aas, xb::bbs ->
      m_argument xa xb >>= (fun () ->
      m_list__m_argument aas bbs
      )
  | [], _
  | _::_, _ ->
      fail ()
(*e: function [[Generic_vs_generic.m_list__m_argument]] *)

(* special case m_arguments when inside a Call(Special(Concat,_), ...)
 * less: factorize with m_list_with_dots? hard because of the special
 * call to Normalize_generic below.
 *)
(*s: function [[Generic_vs_generic.m_arguments_concat]] *)
and m_arguments_concat a b =
  match a,b with
  | [], [] ->
      return ()
  (*s: [[Generic_vs_generic.m_arguments_concat()]] ellipsis cases *)
  (* dots '...' for string literal, can also match no argument *)
  | [A.Arg (A.L (A.String("...", _a)))], [] ->
      return ()

  | A.Arg (A.L (A.String("...", a)))::xsa, B.Arg(bexpr)::xsb ->
    (match Normalize_generic.constant_propagation_and_evaluate_literal bexpr
     with
      | Some _ ->
        (* can match nothing *)
        (m_arguments_concat xsa xsb) >||>
        (* can match more *)
        (m_arguments_concat ((A.Arg (A.L (A.String("...", a))))::xsa) xsb)
      | None ->
        (m_arguments_concat xsa (B.Arg(bexpr)::xsb))
      )
  (*e: [[Generic_vs_generic.m_arguments_concat()]] ellipsis cases *)
  (* the general case *)
  | xa::aas, xb::bbs ->
      m_argument xa xb >>= (fun () ->
      m_arguments_concat aas bbs
      )
  | [], _
  | _::_, _ ->
      fail ()
(*e: function [[Generic_vs_generic.m_arguments_concat]] *)

(*s: function [[Generic_vs_generic.m_argument]] *)
and m_argument a b =
  match a, b with
  (* TODO: iso on keyword argument, keyword is optional in pattern *)

  (* boilerplate *)
  | A.Arg(a1), B.Arg(b1) ->
    m_expr a1 b1
  (*s: [[Generic_vs_generic.m_argument()]] boilerplate cases *)
  | A.ArgType(a1), B.ArgType(b1) ->
    m_type_ a1 b1

  | A.ArgKwd(a1, a2), B.ArgKwd(b1, b2) ->
    m_ident a1 b1 >>= (fun () ->
    m_expr a2 b2
    )
  | A.ArgOther(a1, a2), B.ArgOther(b1, b2) ->
    m_other_argument_operator a1 b1 >>= (fun () ->
    (m_list m_any) a2 b2
    )
  | A.Arg _, _  | A.ArgKwd _, _  | A.ArgType _, _  | A.ArgOther _, _
   -> fail ()
  (*e: [[Generic_vs_generic.m_argument()]] boilerplate cases *)
(*e: function [[Generic_vs_generic.m_argument]] *)

(*s: function [[Generic_vs_generic.m_other_argument_operator]] *)
and m_other_argument_operator = m_other_xxx
(*e: function [[Generic_vs_generic.m_other_argument_operator]] *)

(*****************************************************************************)
(* Type *)
(*****************************************************************************)

(*s: function [[Generic_vs_generic.m_type_]] *)
and m_type_ a b =
  match a, b with
  (*s: [[Generic_vs_generic.m_type_]] metavariable case *)
  | A.TyName ((str,tok), _name_info), t2
     when MV.is_metavar_name str ->
      envf (str, tok) (B.T (t2))
  (*e: [[Generic_vs_generic.m_type_]] metavariable case *)

  (* boilerplate *)
  | A.TyBuiltin(a1), B.TyBuiltin(b1) ->
    (m_wrap m_string) a1 b1
  | A.TyFun(a1, a2), B.TyFun(b1, b2) ->
    (m_list m_parameter_classic) a1 b1 >>= (fun () ->
    m_type_ a2 b2
    )
  | A.TyArray(a1, a2), B.TyArray(b1, b2) ->
    (m_option m_expr) a1 b1 >>= (fun () ->
    m_type_ a2 b2
    )
  | A.TyTuple(a1), B.TyTuple(b1) ->
    (*TODO: m_list__m_type_ ? *)
    (m_bracket (m_list m_type_)) a1 b1

  (*s: [[Generic_vs_generic.m_type_]] boilerplate cases *)
    | A.TyName(a1), B.TyName(b1) ->
      m_name a1 b1
    | A.TyNameApply(a1, a2), B.TyNameApply(b1, b2) ->
      m_name a1 b1 >>= (fun () ->
      m_type_arguments a2 b2
      )
    | A.TyVar(a1), B.TyVar(b1) ->
      m_ident a1 b1

    | A.TyPointer(a0, a1), B.TyPointer(b0, b1) ->
      m_tok a0 b0 >>= (fun () ->
      m_type_ a1 b1
      )

    | A.TyQuestion(a1, a2), B.TyQuestion(b1, b2) ->
      m_type_ a1 b1 >>= (fun () ->
      m_tok a2 b2
      )
    | A.TyRecordAnon(a1), B.TyRecordAnon(b1) ->
      (m_bracket (m_list m_ident_and_type_)) a1 b1
    | A.TyOr (a1, a2, a3), B.TyOr (b1, b2, b3) ->
        m_type_ a1 b1 >>= (fun () ->
        m_tok a2 b2 >>= (fun () ->
        m_type_ a3 b3
        ))
    | A.TyAnd (a1, a2, a3), B.TyAnd (b1, b2, b3) ->
        m_type_ a1 b1 >>= (fun () ->
        m_tok a2 b2 >>= (fun () ->
        m_type_ a3 b3
        ))

    | A.OtherType(a1, a2), B.OtherType(b1, b2) ->
      m_other_type_operator a1 b1 >>= (fun () ->
      (m_list m_any) a2 b2
       )
    | A.TyBuiltin _, _  | A.TyFun _, _  | A.TyNameApply _, _  | A.TyVar _, _
    | A.TyArray _, _  | A.TyPointer _, _ | A.TyTuple _, _  | A.TyQuestion _, _
    | A.TyName _, _ | A.TyOr _, _ | A.TyAnd _, _ | A.TyRecordAnon _, _
    | A.OtherType _, _
     -> fail ()
  (*e: [[Generic_vs_generic.m_type_]] boilerplate cases *)
(*e: function [[Generic_vs_generic.m_type_]] *)

(*s: function [[Generic_vs_generic.m_ident_and_type_]] *)
and m_ident_and_type_ a b =
  match a, b with
  | (a1, a2), (b1, b2) ->
    m_ident a1 b1 >>= (fun () ->
    m_type_ a2 b2
    )
(*e: function [[Generic_vs_generic.m_ident_and_type_]] *)

(*s: function [[Generic_vs_generic.m_type_arguments]] *)
and m_type_arguments a b =
  match a, b with
  (a, b) -> (m_list m_type_argument) a b
(*e: function [[Generic_vs_generic.m_type_arguments]] *)

(*s: function [[Generic_vs_generic.m_type_argument]] *)
and m_type_argument a b =
  match a, b with
  | A.TypeArg(a1), B.TypeArg(b1) ->
    m_type_ a1 b1
  | A.OtherTypeArg(a1, a2), B.OtherTypeArg(b1, b2) ->
    m_other_type_argument_operator a1 b1 >>= (fun () ->
    (m_list m_any) a2 b2
    )
  | A.TypeArg _, _  | A.OtherTypeArg _, _
   -> fail ()
(*e: function [[Generic_vs_generic.m_type_argument]] *)

(*s: function [[Generic_vs_generic.m_other_type_operator]] *)
and m_other_type_operator = m_other_xxx
(*e: function [[Generic_vs_generic.m_other_type_operator]] *)

(*s: function [[Generic_vs_generic.m_other_type_argument_operator]] *)
and m_other_type_argument_operator = m_other_xxx
(*e: function [[Generic_vs_generic.m_other_type_argument_operator]] *)


(*****************************************************************************)
(* Attribute *)
(*****************************************************************************)

(* less: factorize m_list_unordered_keys? but two "keys" here *)
(*s: function [[Generic_vs_generic.m_list__m_attribute]] *)
and m_list__m_attribute (xsa: A.attribute list) (xsb: A.attribute list) =
  match xsa, xsb with
  | [], [] ->
      return ()
  (*s: [[Generic_vs_generic.m_list__m_attribute]] empty list vs list case *)
  (* less-is-ok: *)
  | [], _ -> return ()
  (*e: [[Generic_vs_generic.m_list__m_attribute]] empty list vs list case *)
  (*s: [[Generic_vs_generic.m_list__m_attribute]] [[KeywordAttr]] pattern case *)
  | ((A.KeywordAttr (k, tok)) as a)::xsa, xsb ->
      (try
        let (before, there, after) = xsb |> Common2.split_when (function
            | A.KeywordAttr (k2, _) when k =*= k2 -> true
            | _ -> false) in
        (match there with
        | A.KeywordAttr (x) ->
              m_wrap m_keyword_attribute (k, tok) x >>= (fun () ->
              m_list__m_attribute xsa (before @ after)
              )
        | _ -> raise Impossible
        )
      (*s: [[Generic_vs_generic.m_list__m_attribute]] [[KeywordAttr]] case when [[Not_found]] *)
      with Not_found ->
        (* we now allow some attribute (e.g., Var) to match other (e..g, Let),
         * so we should try all combinations.
         * opti: give an order to each attribute and zip (like in JS AI) *)
        let candidates = all_elem_and_rest_of_list xsb in
        (* less: could use a fold *)
        let rec aux xs =
          match xs with
          | [] -> fail ()
          | (b, xsb)::xs ->
            (m_attribute a b >>= (fun () -> m_list__m_attribute xsa xsb))
             >||> aux xs
        in
        aux candidates
      (*e: [[Generic_vs_generic.m_list__m_attribute]] [[KeywordAttr]] case when [[Not_found]] *)
       )
  (*e: [[Generic_vs_generic.m_list__m_attribute]] [[KeywordAttr]] pattern case *)
  (*s: [[Generic_vs_generic.m_list__m_attribute]] [[NamedAttr]] pattern case *)
  | A.NamedAttr (_, ((s, _) as ida), idinfoa, argsa)::xsa, xsb ->
      (try
        let (before, there, after) = xsb |> Common2.split_when (function
            (* todo: in theory we should resolve the possible alias s2 *)
            | A.NamedAttr (_, (s2, _), _idinfoaliasTODO, _) when s =$= s2 -> true
            | _ -> false) in
        (match there with
        | A.NamedAttr (_, idb, idinfob, argsb) ->
              m_ident ida idb >>= (fun () ->
              (* less: should use m_ident_and_id_info_add_in_env_Expr? *)
              m_id_info idinfoa idinfob >>= (fun () ->
              m_bracket m_list__m_argument argsa argsb >>= (fun () ->
              m_list__m_attribute xsa (before @ after)
              )))
        | _ -> raise Impossible
        )
      with Not_found -> fail ()
      )
  (*e: [[Generic_vs_generic.m_list__m_attribute]] [[NamedAttr]] pattern case *)
  (* the general case *)
  | xa::aas, xb::bbs ->
      m_attribute xa xb >>= (fun () ->
      m_list__m_attribute aas bbs
      )
  | _::_, _ ->
      fail ()
(*e: function [[Generic_vs_generic.m_list__m_attribute]] *)

(*s: function [[Generic_vs_generic.m_keyword_attribute]] *)
and m_keyword_attribute a b =
  match a, b with
 (* equivalent: quite JS-specific *)
  | A.Var, (A.Var | A.Let | A.Const) -> return ()

  | _ -> m_other_xxx a b
(*e: function [[Generic_vs_generic.m_keyword_attribute]] *)

(*s: function [[Generic_vs_generic.m_attribute]] *)
and m_attribute a b =
  match a, b with
  (*s: [[Generic_vs_generic.m_attribute]] resolving alias case *)
  (* equivalence: name resolving! *)
  | a,   B.NamedAttr (t1, _b1, { B.id_resolved =
      {contents = Some ( ( B.ImportedEntity dotted
                         | B.ImportedModule (B.DottedName dotted)
                         ), _sid)}; _}, b2) ->
    let exp = make_dotted dotted in
    m_attribute a (B.OtherAttribute (B.OA_Expr, [B.Tk t1; B.E (B.Call (exp, b2))]))
  (*e: [[Generic_vs_generic.m_attribute]] resolving alias case *)

  (* boilerplate *)
  | A.KeywordAttr(a1), B.KeywordAttr(b1) ->
    m_wrap m_keyword_attribute a1 b1
  | A.NamedAttr(a0, a1, ida, a2), B.NamedAttr(b0, b1, idb, b2) ->
    m_tok a0 b0 >>= (fun () ->
    m_ident a1 b1 >>= (fun () ->
    (* less: should use m_ident_and_id_info_add_in_env_Expr? *)
    m_id_info ida idb >>= (fun () ->
    m_bracket m_list__m_argument a2 b2
    )))
  | A.OtherAttribute(a1, a2), B.OtherAttribute(b1, b2) ->
    m_other_attribute_operator a1 b1 >>= (fun () ->
    (m_list m_any) a2 b2
    )
  | A.KeywordAttr _, _  | A.NamedAttr _, _  | A.OtherAttribute _, _
   -> fail ()
(*e: function [[Generic_vs_generic.m_attribute]] *)

(*s: function [[Generic_vs_generic.m_other_attribute_operator]] *)
and m_other_attribute_operator = m_other_xxx
(*e: function [[Generic_vs_generic.m_other_attribute_operator]] *)


(*****************************************************************************)
(* Statement *)
(*****************************************************************************)
(* possibly go deeper when someone wants that a pattern like
 *   ...
 *   bar();
 * to match also calls to bar() deeply as in
 *   foo();
 *   if(true)
 *      bar();
 *
 * When combined with the deep expr, this even allows to match code like
 *  if(true)
 *     x = bar();
 *
 * This is currently very hacky. We just flatten the list of all substmts.
 *
 * alternatives:
 *   - do it the right way by having '...' work on control-flow paths as in
 *     coccinelle
 *
 * todo? we could restrict ourselves to only a few forms?
 *)
(* experimental! *)

(*s: function [[Generic_vs_generic.m_stmts_deep]] *)
and m_stmts_deep ~less_is_ok (xsa: A.stmt list) (xsb: A.stmt list) =
  (* opti: this was the old code:
   *   if !Flag.go_deeper_stmt && (has_ellipsis_stmts xsa)
   *   then
   *   m_list__m_stmt xsa xsb >!> (fun () ->
   *     let xsb' = SubAST_generic.flatten_substmts_of_stmts xsb in
   *     m_list__m_stmt xsa xsb'
   *   )
   *   else m_list__m_stmt xsa xsb
   *
   * but this was really slow on huge files because with a pattern like
   * 'foo(); ...; bar();' we would call flatten_substmts_of_stmts
   * on each sequences in the program, even though foo(); was not
   * matched first.
   * Better to first match the first element, and if it matches and
   * we have a '...' that was not matched on the current sequence,
   * then we try with flatten_substmts_of_stmts.
   *
   * The code below is mostly a copy paste of m_list__m_stmt. We could
   * factorize, but I prefer to control and limit the number of places
   * where we call m_stmts_deep. Once we call m_list__m_stmt, we
   * are in a simpler world where the list of stmts will not grow.
   *)
  match xsa, xsb with
  | [], [] ->
      return ()
  (* less-is-ok:
   * it's ok to have statements after in the concrete code as long as we
   * matched all the statements in the pattern (there is an implicit
   * '...' at the end, in addition to implicit '...' at the beginning
   * handled by kstmts calling the pattern for each subsequences).
   * TODO: sgrep_generic though then display the whole sequence as a match
   * instead of just the relevant part.
   *)
  | [], _::_ ->
      if less_is_ok then return () else fail ()

  (* dots: '...', can also match no statement *)
  | [A.ExprStmt (A.Ellipsis _i, _)], [] ->
      return ()

  | (A.ExprStmt (A.Ellipsis i, t))::xsa, xb::xsb ->
    (* let's first try the without going deep *)
     (
      (* can match nothing *)
      (m_list__m_stmt xsa (xb::xsb)) >||>
      (* can match more *)
      (env_add_matched_stmt xb >>= (fun () ->
       (m_list__m_stmt ((A.ExprStmt (A.Ellipsis i,t))::xsa) xsb)
      ))
     ) >!> (fun () ->
        if !Flag.go_deeper_stmt
        then
          let xsb' = SubAST_generic.flatten_substmts_of_stmts (xb::xsb) in
          m_list__m_stmt ((A.ExprStmt (A.Ellipsis i, t))::xsa) xsb'
        else fail ()
     )

  (* the general case *)
  | xa::aas, xb::bbs ->
      m_stmt xa xb >>= (fun () ->
        env_add_matched_stmt xb >>= (fun () ->
        m_stmts_deep ~less_is_ok aas bbs
      ))
  | _::_, _ ->
      fail ()

(*e: function [[Generic_vs_generic.m_stmts_deep]] *)

and _m_stmts (xsa: A.stmt list) (xsb: A.stmt list) =
  m_list__m_stmt xsa xsb

(* TODO: factorize with m_list_and_dots less_is_ok = true *)
(*s: function [[Generic_vs_generic.m_list__m_stmt]] *)
and m_list__m_stmt (xsa: A.stmt list) (xsb: A.stmt list) =
  (*s: [[Generic_vs_generic.m_list__m_stmt]] if [[debug]] *)
  if !Flag.debug
  then pr2 (spf "%d vs %d" (List.length xsa) (List.length xsb));
  (*e: [[Generic_vs_generic.m_list__m_stmt]] if [[debug]] *)
  match xsa, xsb with
  | [], [] ->
      return ()
  (*s: [[Generic_vs_generic.m_list__m_stmt()]] empty list vs list case *)
  (* less-is-ok:
   * it's ok to have statements after in the concrete code as long as we
   * matched all the statements in the pattern (there is an implicit
   * '...' at the end, in addition to implicit '...' at the beginning
   * handled by kstmts calling the pattern for each subsequences).
   * TODO: sgrep_generic though then display the whole sequence as a match
   * instead of just the relevant part.
   *)
  | [], _::_ ->
      return ()
  (*e: [[Generic_vs_generic.m_list__m_stmt()]] empty list vs list case *)
  (*s: [[Generic_vs_generic.m_list__m_stmt()]] ellipsis cases *)
  (* dots: '...', can also match no statement *)
  | [A.ExprStmt (A.Ellipsis _i, _)], [] ->
      return ()

  | (A.ExprStmt (A.Ellipsis i, t))::xsa, xb::xsb ->
      (* can match nothing *)
      (m_list__m_stmt xsa (xb::xsb)) >||>
      (* can match more *)
      (env_add_matched_stmt xb >>= (fun () ->
       (m_list__m_stmt ((A.ExprStmt (A.Ellipsis i, t))::xsa) xsb)
      ))
  (*e: [[Generic_vs_generic.m_list__m_stmt()]] ellipsis cases *)
  (* the general case *)
  | xa::aas, xb::bbs ->
      m_stmt xa xb >>= (fun () ->
        env_add_matched_stmt xb >>= (fun () ->
        m_list__m_stmt aas bbs
      ))
  | _::_, _ ->
      fail ()
(*e: function [[Generic_vs_generic.m_list__m_stmt]] *)

(*s: function [[Generic_vs_generic.m_stmt]] *)
and m_stmt a b =
  match a, b with
  (* the order of the matches matters! take care! *)
  (*s: [[Generic_vs_generic.m_stmt()]] disjunction case *)
  (* equivalence: user-defined equivalence! *)
  | A.DisjStmt (a1, a2), b ->
      m_stmt a1 b >||> m_stmt a2 b
  (*e: [[Generic_vs_generic.m_stmt()]] disjunction case *)
  (*s: [[Generic_vs_generic.m_stmt()]] metavariable case *)
  (* metavar: *)
  | A.ExprStmt(A.Id ((str,tok), _id_info), _), b
     when MV.is_metavar_name str ->
      envf (str, tok) (B.S b)
  (*e: [[Generic_vs_generic.m_stmt()]] metavariable case *)
  (*s: [[Generic_vs_generic.m_stmt()]] ellipsis cases *)
  (* dots: '...' can to match any statememt *)
  | A.ExprStmt(A.Ellipsis _i, _), _b ->
      return ()
  (*x: [[Generic_vs_generic.m_stmt()]] ellipsis cases *)
  | A.Return(a0, a1), B.Return(b0, b1) ->
    m_tok a0 b0 >>= (fun () ->
    m_option_ellipsis_ok m_expr a1 b1
    )
  (*e: [[Generic_vs_generic.m_stmt()]] ellipsis cases *)
  (*s: [[Generic_vs_generic.m_stmt()]] deep matching cases *)
  (* deeper: go deep by default implicitly (no need for explicit <... ...>) *)
  | A.ExprStmt(a1, a2), B.ExprStmt(b1, b2) ->
    m_expr_deep a1 b1 >>= (fun () ->
    m_tok a2 b2
    )
  (*x: [[Generic_vs_generic.m_stmt()]] deep matching cases *)
  (* TODO: ... should also allow a subset of stmts *)
  | A.Block(a1), B.Block(b1) ->
    m_bracket (m_stmts_deep ~less_is_ok:false) a1 b1
  (*e: [[Generic_vs_generic.m_stmt()]] deep matching cases *)
  (*s: [[Generic_vs_generic.m_stmt()]] builtin equivalences cases *)
  (* equivalence: vardef ==> assign, and go deep *)
  | A.ExprStmt (a1, _),
    B.DefStmt (ent, B.VarDef ({B.vinit = Some _; _} as def)) ->
      let b1 = AST.vardef_to_assign (ent, def) in
      m_expr_deep a1 b1
  (*x: [[Generic_vs_generic.m_stmt()]] builtin equivalences cases *)
  (* equivalence: *)
  | A.ExprStmt(a1, _), B.Return (_, Some b1) ->
     m_expr_deep a1 b1
  (*e: [[Generic_vs_generic.m_stmt()]] builtin equivalences cases *)

  (* boilerplate *)
  | A.If(a0, a1, a2, a3), B.If(b0, b1, b2, b3) ->
    m_tok a0 b0 >>= (fun () ->
    (* too many regressions doing m_expr_deep by default; Use DeepEllipsis *)
    m_expr a1 b1 >>= (fun () ->
    m_stmt a2 b2 >>= (fun () ->
    (* less-is-more: *)
    m_option_none_can_match_some m_stmt a3 b3
    )))

  | A.While(a0, a1, a2), B.While(b0, b1, b2) ->
    m_tok a0 b0 >>= (fun () ->
    m_expr a1 b1 >>= (fun () ->
    m_stmt a2 b2
    ))
  (*s: [[Generic_vs_generic.m_stmt]] boilerplate cases *)
  | A.DefStmt(a1), B.DefStmt(b1) ->
    m_definition a1 b1
  | A.DirectiveStmt(a1), B.DirectiveStmt(b1) ->
    m_directive a1 b1
  (*x: [[Generic_vs_generic.m_stmt]] boilerplate cases *)

    | A.DoWhile(a0, a1, a2), B.DoWhile(b0, b1, b2) ->
      m_tok a0 b0 >>= (fun () ->
      m_stmt a1 b1 >>= (fun () ->
      m_expr a2 b2
      ))
    | A.For(a0, a1, a2), B.For(b0, b1, b2) ->
      m_tok a0 b0 >>= (fun () ->
      m_for_header a1 b1 >>= (fun () ->
      m_stmt a2 b2
      ))
    | A.Switch(at, a1, a2), B.Switch(bt, b1, b2) ->
      m_tok at bt >>= (fun () ->
      m_option (m_expr) a1 b1 >>= (fun () ->
      (m_list m_case_and_body) a2 b2
      ))

    | A.Continue(a0, a1), B.Continue(b0, b1) ->
      m_tok a0 b0 >>= (fun () ->
      m_label_ident a1 b1
      )
    | A.Break(a0, a1), B.Break(b0, b1) ->
      m_tok a0 b0 >>= (fun () ->
      m_label_ident a1 b1
      )
    | A.Label(a1, a2), B.Label(b1, b2) ->
      m_label a1 b1 >>= (fun () ->
      m_stmt a2 b2
      )
    | A.Goto(a0, a1), B.Goto(b0, b1) ->
      m_tok a0 b0 >>= (fun () ->
      m_label a1 b1
      )
    | A.Throw(a0, a1), B.Throw(b0, b1) ->
      m_tok a0 b0 >>= (fun () ->
      m_expr a1 b1
      )
    | A.Try(a0, a1, a2, a3), B.Try(b0, b1, b2, b3) ->
      m_tok a0 b0 >>= (fun () ->
      m_stmt a1 b1 >>= (fun () ->
      (m_list m_catch) a2 b2 >>= (fun () ->
      (m_option m_finally) a3 b3
      )))
    | A.Assert(a0, a1, a2), B.Assert(b0, b1, b2) ->
      m_tok a0 b0 >>= (fun () ->
      m_expr a1 b1 >>= (fun () ->
      (m_option m_expr) a2 b2
      ))

    | A.OtherStmt(a1, a2), B.OtherStmt(b1, b2) ->
      m_other_stmt_operator a1 b1 >>= (fun () ->
      (m_list m_any) a2 b2
      )
    | A.OtherStmtWithStmt(a1, a2, a3), B.OtherStmtWithStmt(b1, b2, b3) ->
      m_other_stmt_with_stmt_operator a1 b1 >>= (fun () ->
      m_option m_expr a2 b2 >>= (fun () ->
      m_stmt a3 b3
      ))

    | A.ExprStmt _, _  | A.DefStmt _, _  | A.DirectiveStmt _, _
    | A.Block _, _  | A.If _, _  | A.While _, _  | A.DoWhile _, _  | A.For _, _
    | A.Switch _, _  | A.Return _, _  | A.Continue _, _  | A.Break _, _
    | A.Label _, _  | A.Goto _, _  | A.Throw _, _  | A.Try _, _  | A.Assert _, _
    | A.OtherStmt _, _ | A.OtherStmtWithStmt _, _
     -> fail ()
  (*e: [[Generic_vs_generic.m_stmt]] boilerplate cases *)
(*e: function [[Generic_vs_generic.m_stmt]] *)

(*s: function [[Generic_vs_generic.m_for_header]] *)
and m_for_header a b =
  match a, b with
  (* dots: *)
  | A.ForEllipsis _, _ -> return ()

  | A.ForClassic(a1, a2, a3), B.ForClassic(b1, b2, b3) ->
    (m_list m_for_var_or_expr) a1 b1 >>= (fun () ->
    m_option m_expr a2 b2 >>= (fun () ->
    m_option m_expr a3 b3
    ))
  | A.ForEach(a1, at, a2), B.ForEach(b1, bt, b2) ->
    m_pattern a1 b1 >>= (fun () ->
    m_tok at bt >>= (fun () ->
    m_expr a2 b2
    ))
  | A.ForClassic _, _  | A.ForEach _, _
   -> fail ()
(*e: function [[Generic_vs_generic.m_for_header]] *)

(*s: function [[Generic_vs_generic.m_for_var_or_expr]] *)
and m_for_var_or_expr a b =
  match a, b with
  | A.ForInitVar(a1, a2), B.ForInitVar(b1, b2) ->
    m_entity a1 b1 >>= (fun () ->
    m_variable_definition a2 b2
    )
  | A.ForInitExpr(a1), B.ForInitExpr(b1) ->
    m_expr a1 b1
  | A.ForInitVar _, _  | A.ForInitExpr _, _
   -> fail ()
(*e: function [[Generic_vs_generic.m_for_var_or_expr]] *)

(*s: function [[Generic_vs_generic.m_label]] *)
and m_label a b =
  match a, b with
  (a, b) -> m_ident a b
(*e: function [[Generic_vs_generic.m_label]] *)

(*s: function [[Generic_vs_generic.m_catch]] *)
and m_catch a b =
  match a, b with
  | (at, a1, a2), (bt, b1, b2) ->
    m_tok at bt >>= (fun () ->
    m_pattern a1 b1 >>= (fun () ->
    m_stmt a2 b2
     ))
(*e: function [[Generic_vs_generic.m_catch]] *)

(*s: function [[Generic_vs_generic.m_finally]] *)
and m_finally a b =
  match a, b with
  ((at, a), (bt, b)) ->
      m_tok at bt >>= (fun () ->
      m_stmt a b
      )
(*e: function [[Generic_vs_generic.m_finally]] *)

(*s: function [[Generic_vs_generic.m_case_and_body]] *)
and m_case_and_body a b =
  match a, b with
  | (a1, a2), (b1, b2) ->
    (m_list m_case) a1 b1 >>= (fun () ->
    m_stmt a2 b2
    )
(*e: function [[Generic_vs_generic.m_case_and_body]] *)

(*s: function [[Generic_vs_generic.m_case]] *)
and m_case a b =
  match a, b with
  | A.Case(a0, a1), B.Case(b0, b1) ->
    m_tok a0 b0 >>= (fun () ->
    m_pattern a1 b1
    )
  | A.CaseEqualExpr(a0, a1), B.CaseEqualExpr(b0, b1) ->
    m_tok a0 b0 >>= (fun () ->
    m_expr a1 b1
    )
  | A.Default a0, B.Default b0 ->
    m_tok a0 b0
  | A.Case _, _  | A.Default _, _ | A.CaseEqualExpr _, _
   -> fail ()
(*e: function [[Generic_vs_generic.m_case]] *)

(*s: function [[Generic_vs_generic.m_other_stmt_operator]] *)
and m_other_stmt_operator = m_other_xxx
(*e: function [[Generic_vs_generic.m_other_stmt_operator]] *)

(*s: function [[Generic_vs_generic.m_other_stmt_with_stmt_operator]] *)
and m_other_stmt_with_stmt_operator = m_other_xxx
(*e: function [[Generic_vs_generic.m_other_stmt_with_stmt_operator]] *)


(*****************************************************************************)
(* Pattern *)
(*****************************************************************************)

(*s: function [[Generic_vs_generic.m_pattern]] *)
and m_pattern a b =
  match a, b with
  (*s: [[Generic_vs_generic.m_pattern()]] disjunction case *)
  (* equivalence: user-defined equivalence! *)
  | A.DisjPat (a1, a2), b ->
      m_pattern a1 b >||> m_pattern a2 b
  (*e: [[Generic_vs_generic.m_pattern()]] disjunction case *)
  (*s: [[Generic_vs_generic.m_pattern()]] metavariable case *)
  (* metavar: *)
  | A.PatId ((str,tok), _id_info), b2
     when MV.is_metavar_name str ->
      (try
        let e2 = AST.pattern_to_expr b2 in
        envf (str, tok) (B.E (e2))
       (* this can happen with PatAs in exception handler in Python *)
       with AST.NotAnExpr ->
        envf (str, tok) (B.P b2)
      )
  (*e: [[Generic_vs_generic.m_pattern()]] metavariable case *)

  (* boilerplate *)
  (*s: [[Generic_vs_generic.m_pattern]] boilerplate cases *)
    | A.PatId(a1, a2), B.PatId(b1, b2) ->
      m_ident a1 b1 >>= (fun () ->
      m_id_info a2 b2
      )
    | A.PatLiteral(a1), B.PatLiteral(b1) ->
      m_literal a1 b1
    | A.PatType(a1), B.PatType(b1) ->
      m_type_ a1 b1
    | A.PatConstructor(a1, a2), B.PatConstructor(b1, b2) ->
      m_name a1 b1 >>= (fun () ->
      (m_list m_pattern) a2 b2
      )
    | A.PatTuple(a1), B.PatTuple(b1) ->
      (m_list m_pattern) a1 b1
    | A.PatList(a1), B.PatList(b1) ->
      m_bracket (m_list m_pattern) a1 b1
    | A.PatRecord(a1), B.PatRecord(b1) ->
      m_bracket (m_list m_field_pattern) a1 b1
    | A.PatKeyVal(a1, a2), B.PatKeyVal(b1, b2) ->
      m_pattern a1 b1 >>= (fun () ->
      m_pattern a2 b2
      )
    | A.PatUnderscore(a1), B.PatUnderscore(b1) ->
      m_tok a1 b1
    | A.PatDisj(a1, a2), B.PatDisj(b1, b2) ->
      m_pattern a1 b1 >>= (fun () ->
      m_pattern a2 b2
      )
    | A.PatAs(a1, (a2, a3)), B.PatAs(b1, (b2, b3)) ->
      m_pattern a1 b1 >>= (fun () ->
      m_ident_and_id_info_add_in_env_Expr (a2, a3) (b2, b3)
      )
    | A.PatTyped(a1, a2), B.PatTyped(b1, b2) ->
      m_pattern a1 b1 >>= (fun () ->
      m_type_ a2 b2
      )
    | A.PatVar(a1, a2), B.PatVar(b1, b2) ->
      m_type_ a1 b1 >>= (fun () ->
      m_option m_ident_and_id_info_add_in_env_Expr a2 b2
      )
    | A.PatWhen(a1, a2), B.PatWhen(b1, b2) ->
      m_pattern a1 b1 >>= (fun () ->
      m_expr a2 b2
      )
    | A.OtherPat(a1, a2), B.OtherPat(b1, b2) ->
      m_other_pattern_operator a1 b1 >>= (fun () ->
      (m_list m_any) a2 b2
      )
    | A.PatId _, _  | A.PatLiteral _, _  | A.PatConstructor _, _
    | A.PatTuple _, _  | A.PatList _, _  | A.PatRecord _, _  | A.PatKeyVal _, _
    | A.PatUnderscore _, _  | A.PatDisj _, _  | A.PatWhen _, _  | A.PatAs _, _
    | A.PatTyped _, _  | A.OtherPat _, _ | A.PatType _, _ | A.PatVar _, _
     -> fail ()
  (*e: [[Generic_vs_generic.m_pattern]] boilerplate cases *)
(*e: function [[Generic_vs_generic.m_pattern]] *)

(*s: function [[Generic_vs_generic.m_field_pattern]] *)
and m_field_pattern a b =
  match a, b with
  | (a1, a2), (b1, b2) ->
    m_name a1 b1 >>= (fun () ->
    m_pattern a2 b2
    )
(*e: function [[Generic_vs_generic.m_field_pattern]] *)

(*s: function [[Generic_vs_generic.m_other_pattern_operator]] *)
and m_other_pattern_operator = m_other_xxx
(*e: function [[Generic_vs_generic.m_other_pattern_operator]] *)


(*****************************************************************************)
(* Definitions *)
(*****************************************************************************)

(*s: function [[Generic_vs_generic.m_definition]] *)
and m_definition a b =
  match a, b with
  | (a1, a2), (b1, b2) ->
    m_entity a1 b1 >>= (fun () ->
    m_definition_kind a2 b2
    )
(*e: function [[Generic_vs_generic.m_definition]] *)

(*s: function [[Generic_vs_generic.m_entity]] *)
and m_entity a b =
  match a, b with
  (* bugfix: when we use a metavar to match an entity, as in $X(...): ...
   * and later we use $X again to match a name, the $X is first an ident and
   * later an expression, which would prevent a match. Instead we need to
   * make $X an expression early on
   *)
  { A. name = a1; attrs = a2; tparams = a4; info = a5 },
  { B. name = b1; attrs = b2; tparams = b4; info = b5 } ->
    m_ident_and_id_info_add_in_env_Expr (a1, a5) (b1, b5) >>= (fun () ->
    (m_list__m_attribute) a2 b2 >>= (fun () ->
    (m_list m_type_parameter) a4 b4
    ))
(*e: function [[Generic_vs_generic.m_entity]] *)

(*s: function [[Generic_vs_generic.m_definition_kind]] *)
and m_definition_kind a b =
  match a, b with
  (* boilerplate *)
  | A.FuncDef(a1), B.FuncDef(b1) ->
    m_function_definition a1 b1
  | A.VarDef(a1), B.VarDef(b1) ->
    m_variable_definition a1 b1
  | A.FieldDef(a1), B.FieldDef(b1) ->
    m_variable_definition a1 b1
  | A.ClassDef(a1), B.ClassDef(b1) ->
    m_class_definition a1 b1
  (*s: [[Generic_vs_generic.m_definition_kind]] boilerplate cases *)
  | A.TypeDef(a1), B.TypeDef(b1) ->
    m_type_definition a1 b1
  | A.ModuleDef(a1), B.ModuleDef(b1) ->
    m_module_definition a1 b1
  | A.MacroDef(a1), B.MacroDef(b1) ->
    m_macro_definition a1 b1
  | A.Signature(a1), B.Signature(b1) ->
    m_type_ a1 b1
  | A.UseOuterDecl a1, B.UseOuterDecl b1 ->
    m_tok a1 b1
  | A.FuncDef _, _ | A.VarDef _, _  | A.ClassDef _, _  | A.TypeDef _, _
  | A.ModuleDef _, _  | A.MacroDef _, _  | A.Signature _, _
  | A.UseOuterDecl _, _
  | A.FieldDef _, _
   -> fail ()
  (*e: [[Generic_vs_generic.m_definition_kind]] boilerplate cases *)
(*e: function [[Generic_vs_generic.m_definition_kind]] *)

(*s: function [[Generic_vs_generic.m_type_parameter_constraint]] *)
and m_type_parameter_constraint a b =
  match a, b with
  | A.Extends(a1), B.Extends(b1) ->
    m_type_ a1 b1
(*e: function [[Generic_vs_generic.m_type_parameter_constraint]] *)

(*s: function [[Generic_vs_generic.m_type_parameter_constraints]] *)
and m_type_parameter_constraints a b =
  match a, b with
  (a, b) -> (m_list m_type_parameter_constraint) a b
(*e: function [[Generic_vs_generic.m_type_parameter_constraints]] *)

(*s: function [[Generic_vs_generic.m_type_parameter]] *)
and m_type_parameter a b =
  match a, b with
  | (a1, a2), (b1, b2) ->
    m_ident a1 b1 >>= (fun () ->
    m_type_parameter_constraints a2 b2
    )
(*e: function [[Generic_vs_generic.m_type_parameter]] *)


(* ------------------------------------------------------------------------- *)
(* Function (or method) definition *)
(* ------------------------------------------------------------------------- *)

(*s: function [[Generic_vs_generic.m_function_definition]] *)
and m_function_definition a b =
  match a, b with
  { A. fparams = a1; frettype = a2; fbody = a3; },
  { B. fparams = b1; frettype = b2; fbody = b3; } ->
    m_parameters a1 b1 >>= (fun () ->
    (m_option_none_can_match_some m_type_) a2 b2 >>= (fun () ->
    m_stmt a3 b3
    ))
(*e: function [[Generic_vs_generic.m_function_definition]] *)

(*s: function [[Generic_vs_generic.m_parameters]] *)
and m_parameters a b =
  m_list_with_dots m_parameter
    (function A.ParamEllipsis _ -> true | _ -> false)
  false (* empty list can not match non-empty list *)
  a b
(*e: function [[Generic_vs_generic.m_parameters]] *)

(*s: function [[Generic_vs_generic.m_parameter]] *)
and m_parameter a b =
  match a, b with
  (* boilerplate *)
  | A.ParamClassic(a1), B.ParamClassic(b1) ->
    m_parameter_classic a1 b1
  (*s: [[Generic_vs_generic.m_parameter]] boilerplate cases *)
  | A.ParamPattern(a1), B.ParamPattern(b1) ->
    m_pattern a1 b1
  | A.OtherParam(a1, a2), B.OtherParam(b1, b2) ->
    m_other_parameter_operator a1 b1 >>= (fun () ->
    (m_list m_any) a2 b2
    )
  | A.ParamEllipsis(a1), B.ParamEllipsis(b1) ->
    m_tok a1 b1
  | A.ParamClassic _, _  | A.ParamPattern _, _
  | A.ParamEllipsis _, _
  | A.OtherParam _, _
   -> fail ()
  (*e: [[Generic_vs_generic.m_parameter]] boilerplate cases *)
(*e: function [[Generic_vs_generic.m_parameter]] *)

(*s: function [[Generic_vs_generic.m_parameter_classic]] *)
and m_parameter_classic a b =
  match a, b with
  (*s: [[Generic_vs_generic.m_parameter_classic]] metavariable case *)
  (* bugfix: when we use a metavar to match a parameter, as in foo($X): ...
   * and later we use $X again to match a name, the $X is first an ident and
   * later an expression, which would prevent a match. Instead we need to
   * make $X an expression early on
   *)

  | { A. pname = Some a1; pdefault = a2; ptype = a3; pattrs = a4; pinfo = a5 },
    { B. pname = Some b1; pdefault = b2; ptype = b3; pattrs = b4; pinfo = b5 }
    ->
     m_ident_and_id_info_add_in_env_Expr (a1, a5) (b1, b5) >>= (fun () ->
     (m_option m_expr) a2 b2 >>= (fun () ->
     (m_option_none_can_match_some m_type_) a3 b3 >>= (fun () ->
     (m_list__m_attribute) a4 b4
     )))
  (*e: [[Generic_vs_generic.m_parameter_classic]] metavariable case *)

  (* boilerplate *)
  | { A. pname = a1; pdefault = a2; ptype = a3; pattrs = a4; pinfo = a5 },
    { B. pname = b1; pdefault = b2; ptype = b3; pattrs = b4; pinfo = b5 } ->
    (m_option m_ident) a1 b1 >>= (fun () ->
    (m_option m_expr) a2 b2 >>= (fun () ->
    (m_option m_type_) a3 b3 >>= (fun () ->
    (m_list__m_attribute) a4 b4 >>= (fun () ->
    m_id_info a5 b5
    ))))
(*e: function [[Generic_vs_generic.m_parameter_classic]] *)

(*s: function [[Generic_vs_generic.m_other_parameter_operator]] *)
and m_other_parameter_operator = m_other_xxx
(*e: function [[Generic_vs_generic.m_other_parameter_operator]] *)


(* ------------------------------------------------------------------------- *)
(* Variable definition *)
(* ------------------------------------------------------------------------- *)

(*s: function [[Generic_vs_generic.m_variable_definition]] *)
and m_variable_definition a b =
  match a, b with
  (* boilerplate *)
  { A. vinit = a1; vtype = a2; },
  { B. vinit = b1; vtype = b2; } ->
    (m_option m_expr) a1 b1 >>= (fun () ->
    (m_option m_type_) a2 b2
    )
(*e: function [[Generic_vs_generic.m_variable_definition]] *)


(* ------------------------------------------------------------------------- *)
(* Field definition and use *)
(* ------------------------------------------------------------------------- *)

(* As opposed to statements, the order of fields should not matter.
 *
 * We actually filter the '...' and use a less-is-ok approach.
 * Indeed '...' are not really useful, and in some patological cases they
 * were actually leading to the use a tons of memory. Indeed, in certain
 * files containing a long list of fields (like 3000 static fields),
 * the classic use of >||> to handle Ellipsis variations were stressing
 * a lot the engine. Simpler to just filter them.
 *)
(*s: function [[Generic_vs_generic.m_fields]] *)
and m_fields (xsa: A.field list) (xsb: A.field list) =
  (* let's filter the '...' *)
  let xsa = xsa |> Common.exclude (function
      | A.FieldStmt (A.ExprStmt (A.Ellipsis _, _)) -> true
      | _ -> false) in
  m_list__m_field xsa xsb
(*e: function [[Generic_vs_generic.m_fields]] *)

(* less: mix of m_list_and_dots and m_list_unordered_keys, hard to factorize *)
(*s: function [[Generic_vs_generic.m_list__m_field]] *)
and m_list__m_field (xsa: A.field list) (xsb: A.field list) =
  match xsa, xsb with
  | [], [] ->
      return ()
  (*s: [[Generic_vs_generic.m_list__m_field()]] empty list vs list case *)
  (* less-is-ok:
   * it's ok to have fields after in the concrete code as long as we
   * matched all the fields in the pattern.
   * TODO? should we impose to use '...' if you allow extra fields?
   *)
  | [], _::_ ->
      return ()
  (*e: [[Generic_vs_generic.m_list__m_field()]] empty list vs list case *)
  (*s: [[Generic_vs_generic.m_list__m_field()]] ellipsis cases *)
  | (A.FieldStmt (A.ExprStmt (A.Ellipsis _, _)))::_, _ ->
      raise Impossible
  (*e: [[Generic_vs_generic.m_list__m_field()]] ellipsis cases *)
  (*s: [[Generic_vs_generic.m_list__m_field()]] [[DefStmt]] pattern case *)
  | (A.FieldStmt (A.DefStmt (({A.name = (s1, _); _}, _) as adef)) as a)::xsa,
     xsb ->
     (*s: [[Generic_vs_generic.m_list__m_field()]] in [[DefStmt]] case if metavar field *)
     if MV.is_metavar_name s1 || Matching_generic.is_regexp_string s1
     then
        let candidates = all_elem_and_rest_of_list xsb in
        (* less: could use a fold *)
        let rec aux xs =
          match xs with
          | [] -> fail ()
          | (b, xsb)::xs ->
              (m_field a b >>= (fun () -> m_list__m_field xsa xsb))
              >||> aux xs
        in
        aux candidates
     (*e: [[Generic_vs_generic.m_list__m_field()]] in [[DefStmt]] case if metavar field *)
     else
      (try
        let (before, there, after) = xsb |> Common2.split_when (function
            | (A.FieldStmt (A.DefStmt ({B.name = (s2, _tok); _}, _)))
                when s2 = s1 -> true
            | _ -> false
        ) in
        (match there with
        | (A.FieldStmt (A.DefStmt bdef)) ->
           m_definition adef bdef >>= (fun () ->
           m_list__m_field xsa (before @ after)
           )
        | _ -> raise Impossible
        )
      with Not_found -> fail ()
      )
  (*e: [[Generic_vs_generic.m_list__m_field()]] [[DefStmt]] pattern case *)
  (* the general case *)
  | xa::aas, xb::bbs ->
      m_field xa xb >>= (fun () ->
      m_list__m_field aas bbs
      )
  | _::_, _ ->
      fail ()
(*e: function [[Generic_vs_generic.m_list__m_field]] *)

(*s: function [[Generic_vs_generic.m_field]] *)
and m_field a b =
  match a, b with
  (* boilerplate *)
  | A.FieldStmt(a1), B.FieldStmt(b1) ->
    m_stmt a1 b1
  (*s: [[Generic_vs_generic.m_field]] boilerplate cases *)
  | A.FieldDynamic(a1, a2, a3), B.FieldDynamic(b1, b2, b3) ->
    m_expr a1 b1 >>= (fun () ->
    (m_list__m_attribute) a2 b2 >>= (fun () ->
    m_expr a3 b3
    ))
  | A.FieldSpread(a0, a1), B.FieldSpread(b0, b1) ->
    m_tok a0 b0 >>= (fun () ->
    m_expr a1 b1
    )
  | A.FieldDynamic _, _
  | A.FieldSpread _, _ | A.FieldStmt _, _
   -> fail ()
  (*e: [[Generic_vs_generic.m_field]] boilerplate cases *)
(*e: function [[Generic_vs_generic.m_field]] *)


(* ------------------------------------------------------------------------- *)
(* Type definition *)
(* ------------------------------------------------------------------------- *)

(*s: function [[Generic_vs_generic.m_type_definition]] *)
and m_type_definition a b =
  match a, b with
  { A. tbody = a1;},
  { B. tbody = b1; } ->
    m_type_definition_kind a1 b1
(*e: function [[Generic_vs_generic.m_type_definition]] *)

(*s: function [[Generic_vs_generic.m_type_definition_kind]] *)
and m_type_definition_kind a b =
  match a, b with
  | A.OrType(a1), B.OrType(b1) ->
    (m_list m_or_type) a1 b1
  | A.AndType(a1), B.AndType(b1) ->
    m_bracket (m_fields) a1 b1
  | A.AliasType(a1), B.AliasType(b1) ->
    m_type_ a1 b1
  | A.NewType(a1), B.NewType(b1) ->
    m_type_ a1 b1
  | A.Exception(a1, a2), B.Exception(b1, b2) ->
    m_ident a1 b1 >>= (fun () ->
    (* TODO: m_list__m_type_ ? *)
    (m_list m_type_) a2 b2
    )
  | A.OtherTypeKind(a1, a2), B.OtherTypeKind(b1, b2) ->
    m_other_type_kind_operator a1 b1 >>= (fun () ->
    (m_list m_any) a2 b2
    )
  | A.OrType _, _ | A.AndType _, _ | A.AliasType _, _ | A.Exception _, _
  | A.NewType _, _
  | A.OtherTypeKind _, _
   -> fail ()
(*e: function [[Generic_vs_generic.m_type_definition_kind]] *)

(*s: function [[Generic_vs_generic.m_or_type]] *)
and m_or_type a b =
  match a, b with
  | A.OrConstructor(a1, a2), B.OrConstructor(b1, b2) ->
    (m_ident) a1 b1 >>= (fun () ->
    (* TODO: m_list__m_type_ ? *)
    (m_list m_type_) a2 b2
    )
  | A.OrEnum(a1, a2), B.OrEnum(b1, b2) ->
    m_ident a1 b1 >>= (fun () ->
    m_option m_expr a2 b2
    )
  | A.OrUnion(a1, a2), B.OrUnion(b1, b2) ->
    m_ident a1 b1 >>= (fun () ->
    (m_type_) a2 b2
    )
  | A.OtherOr(a1, a2), B.OtherOr(b1, b2) ->
    m_other_or_type_element_operator a1 b1 >>= (fun () ->
    (m_list m_any) a2 b2
    )
  | A.OrConstructor _, _ | A.OrEnum _, _ | A.OrUnion _, _ | A.OtherOr _, _
   -> fail ()
(*e: function [[Generic_vs_generic.m_or_type]] *)

(*s: function [[Generic_vs_generic.m_other_type_kind_operator]] *)
and m_other_type_kind_operator = m_other_xxx
(*e: function [[Generic_vs_generic.m_other_type_kind_operator]] *)

(*s: function [[Generic_vs_generic.m_other_or_type_element_operator]] *)
and m_other_or_type_element_operator = m_other_xxx
(*e: function [[Generic_vs_generic.m_other_or_type_element_operator]] *)

(*s: function [[Generic_vs_generic.m_list__m_type_]] *)
and m_list__m_type_ (xsa: A.type_ list) (xsb: A.type_ list) =
  m_list_with_dots m_type_
   (* dots: '...', this is very Python Specific I think *)
   (function
     | A.OtherType (A.OT_Arg, [A.Ar (A.Arg(A.Ellipsis _i))]) -> true
     | _ -> false
   )
  (* less-is-ok: it's ok to not specify all the parents I think *)
  true (* empty list can not match non-empty list *)
  xsa xsb
(*e: function [[Generic_vs_generic.m_list__m_type_]] *)

(* ------------------------------------------------------------------------- *)
(* Class definition *)
(* ------------------------------------------------------------------------- *)
(* TODO: there are a few remaining m_list m_type_ we could transform
 * to use instead m_list__m_type_, for Exception, TyTuple and OrConstructor
 * but maybe quite different from list of types in inheritance
 * TODO again like for m_list__m_field we should not care about the
 * order here.
 *)

(*s: function [[Generic_vs_generic.m_class_definition]] *)
and m_class_definition a b =
  match a, b with
  { A. ckind = a1; cextends = a2; cimplements = a3; cbody = a4;
      cmixins = a5;
    },
  { B. ckind = b1; cextends = b2; cimplements = b3; cbody = b4;
      cmixins = b5;
    } ->
    m_class_kind a1 b1 >>= (fun () ->
    (m_list__m_type_) a2 b2 >>= (fun () ->
    (m_list__m_type_) a3 b3 >>= (fun () ->
    (m_list__m_type_) a5 b5 >>= (fun () ->
    m_bracket (m_fields) a4 b4
    ))))
(*e: function [[Generic_vs_generic.m_class_definition]] *)

(*s: function [[Generic_vs_generic.m_class_kind]] *)
and m_class_kind a b =
  match a, b with
  | A.Class, B.Class ->
    return ()
  | A.Interface, B.Interface ->
    return ()
  | A.Trait, B.Trait ->
    return ()
  | A.Class, _ | A.Interface, _ | A.Trait, _
   -> fail ()
(*e: function [[Generic_vs_generic.m_class_kind]] *)


(* ------------------------------------------------------------------------- *)
(* Module definition *)
(* ------------------------------------------------------------------------- *)

(*s: function [[Generic_vs_generic.m_module_definition]] *)
and m_module_definition a b =
  match a, b with
  { A. mbody = a1; },
  { B. mbody = b1; } ->
    m_module_definition_kind a1 b1
(*e: function [[Generic_vs_generic.m_module_definition]] *)

(*s: function [[Generic_vs_generic.m_module_definition_kind]] *)
and m_module_definition_kind a b =
  match a, b with
  | A.ModuleAlias(a1), B.ModuleAlias(b1) ->
    m_name a1 b1
  | A.ModuleStruct(a1, a2), B.ModuleStruct(b1, b2) ->
    (m_option m_dotted_name) a1 b1 >>= (fun () ->
    (m_list m_item) a2 b2
    )
  | A.OtherModule(a1, a2), B.OtherModule(b1, b2) ->
    m_other_module_operator a1 b1 >>= (fun () ->
    (m_list m_any) a2 b2
    )
  | A.ModuleAlias _, _ | A.ModuleStruct _, _ | A.OtherModule _, _
   -> fail ()
(*e: function [[Generic_vs_generic.m_module_definition_kind]] *)

(*s: function [[Generic_vs_generic.m_other_module_operator]] *)
and m_other_module_operator = m_other_xxx
(*e: function [[Generic_vs_generic.m_other_module_operator]] *)


(* ------------------------------------------------------------------------- *)
(* Macro definition *)
(* ------------------------------------------------------------------------- *)

(*s: function [[Generic_vs_generic.m_macro_definition]] *)
and m_macro_definition a b =
  match a, b with
  { A. macroparams = a1; macrobody = a2; },
  { B. macroparams = b1; macrobody = b2; } ->
    (m_list m_ident) a1 b1 >>= (fun () ->
    (m_list m_any) a2 b2
    )
(*e: function [[Generic_vs_generic.m_macro_definition]] *)


(*****************************************************************************)
(* Directives (Module import/export, macros) *)
(*****************************************************************************)

(*s: function [[Generic_vs_generic.m_directive]] *)
and m_directive a b =
  m_directive_basic a b >!> (fun () ->
    match a with
    (* normalize only if very simple import pattern (no alias) *)
    | A.ImportFrom (_, _, _, None) | A.ImportAs (_, _, None)
      ->
      (* equivalence: *)
      let normal_a = Normalize_generic.normalize_import_opt true a in
      let normal_b = Normalize_generic.normalize_import_opt false b in
      (match normal_a, normal_b with
      | Some (a0, a1), Some (b0, b1) ->
        m_tok a0 b0 >>= (fun () ->
        m_module_name_prefix a1 b1
        )
      | _ -> fail ()
      )
   (* more complex pattern should not be normalized *)
   | A.ImportFrom _ | A.ImportAs _
   (* definitely do not normalize the pattern for ImportAll *)
   | A.ImportAll _
   | A.Package _ | A.PackageEnd _ | A.OtherDirective _ ->
      fail ()
  )
(*e: function [[Generic_vs_generic.m_directive]] *)


(* less-is-ok: a few of these below with the use of m_module_name_prefix and
 * m_option_none_can_match_some.
 * todo? not sure it makes sense to always allow m_module_name_prefix below
 *)
(*s: function [[Generic_vs_generic.m_directive_basic]] *)
and m_directive_basic a b =
  match a, b with
  (*s: [[Generic_vs_generic.m_directive_basic]] import cases *)
  | A.ImportFrom(a0, a1, a2, a3), B.ImportFrom(b0, b1, b2, b3) ->
    m_tok a0 b0 >>= (fun () ->
    m_module_name_prefix a1 b1 >>= (fun () ->
    m_ident_and_empty_id_info_add_in_env_Expr a2 b2 >>= (fun () ->
    (m_option_none_can_match_some m_ident_and_empty_id_info_add_in_env_Expr)
                  a3 b3
    )))
  | A.ImportAs(a0, a1, a2), B.ImportAs(b0, b1, b2) ->
    m_tok a0 b0 >>= (fun () ->
    m_module_name_prefix a1 b1 >>= (fun () ->
    (m_option_none_can_match_some m_ident_and_empty_id_info_add_in_env_Expr)
              a2 b2
    ))
  | A.ImportAll(a0, a1, a2), B.ImportAll(b0, b1, b2) ->
    m_tok a0 b0 >>= (fun () ->
    m_module_name_prefix a1 b1 >>= (fun () ->
    m_tok a2 b2
    ))
  (*e: [[Generic_vs_generic.m_directive_basic]] import cases *)
  (* boilerplate *)
  (*s: [[Generic_vs_generic.m_directive_basic]] boilerplate cases *)
  | A.Package(a0, a1), B.Package(b0, b1) ->
    m_tok a0 b0 >>= (fun () ->
    m_dotted_name a1 b1
    )
  | A.PackageEnd a1, B.PackageEnd b1 ->
      m_tok a1 b1
  | A.OtherDirective(a1, a2), B.OtherDirective(b1, b2) ->
    m_other_directive_operator a1 b1 >>= (fun () ->
    (m_list m_any) a2 b2
    )
  | A.ImportFrom _, _ | A.ImportAs _, _ | A.OtherDirective _, _
  | A.ImportAll _, _ | A.Package _, _ | A.PackageEnd _, _
   -> fail ()
  (*e: [[Generic_vs_generic.m_directive_basic]] boilerplate cases *)
(*e: function [[Generic_vs_generic.m_directive_basic]] *)

(*s: function [[Generic_vs_generic.m_other_directive_operator]] *)
and m_other_directive_operator = m_other_xxx
(*e: function [[Generic_vs_generic.m_other_directive_operator]] *)

(*****************************************************************************)
(* Toplevel *)
(*****************************************************************************)

(*s: function [[Generic_vs_generic.m_item]] *)
and m_item a b = m_stmt a b
(*e: function [[Generic_vs_generic.m_item]] *)

(*s: function [[Generic_vs_generic.m_program]] *)
and m_program a b =
  match a, b with
  (a, b) -> (m_list m_item) a b
(*e: function [[Generic_vs_generic.m_program]] *)

(*****************************************************************************)
(* Any *)
(*****************************************************************************)

(*s: function [[Generic_vs_generic.m_any]] *)
and m_any a b =
  match a, b with
  | A.Ss(a1), B.Ss(b1) ->
    m_stmts_deep ~less_is_ok:true a1 b1
  | A.E(a1), B.E(b1) ->
    m_expr a1 b1
  | A.S(a1), B.S(b1) ->
    m_stmt a1 b1
  (* boilerplate *)
  (*s: [[Generic_vs_generic.m_any]] boilerplate cases *)
  | A.N(a1), B.N(b1) ->
    m_name a1 b1
  | A.Modn(a1), B.Modn(b1) ->
    m_module_name a1 b1
  | A.Tk(a1), B.Tk(b1) ->
    m_tok a1 b1
  | A.Di(a1), B.Di(b1) ->
    m_dotted_name a1 b1
  | A.En(a1), B.En(b1) ->
    m_entity a1 b1
  | A.T(a1), B.T(b1) ->
    m_type_ a1 b1
  | A.P(a1), B.P(b1) ->
    m_pattern a1 b1
  | A.Def(a1), B.Def(b1) ->
    m_definition a1 b1
  | A.Dir(a1), B.Dir(b1) ->
    m_directive a1 b1
  | A.Fld(a1), B.Fld(b1) ->
    m_field a1 b1
  | A.Pa(a1), B.Pa(b1) ->
    m_parameter a1 b1
  | A.Ar(a1), B.Ar(b1) ->
    m_argument a1 b1
  | A.At(a1), B.At(b1) ->
    m_attribute a1 b1
  | A.Dk(a1), B.Dk(b1) ->
    m_definition_kind a1 b1
  | A.Pr(a1), B.Pr(b1) ->
    m_program a1 b1
  | A.I(a1), B.I(b1) ->
    m_ident a1 b1
  | A.Lbli(a1), B.Lbli(b1) ->
    m_label_ident a1 b1
  | A.Fldi(a1), B.Fldi(b1) ->
    m_field_ident a1 b1
  | A.I _, _  | A.N _, _  | A.Modn _, _ | A.Di _, _  | A.En _, _  | A.E _, _
  | A.S _, _  | A.T _, _  | A.P _, _  | A.Def _, _  | A.Dir _, _
  | A.Pa _, _  | A.Ar _, _  | A.At _, _  | A.Dk _, _ | A.Pr _, _
  | A.Fld _, _ | A.Ss _, _ | A.Tk _, _ | A.Lbli _, _ | A.Fldi _, _
   -> fail ()
  (*e: [[Generic_vs_generic.m_any]] boilerplate cases *)
(*e: function [[Generic_vs_generic.m_any]] *)
(*e: semgrep/matching/Generic_vs_generic.ml *)
